Conversation
Implements Optuna integration to automate hyperparameter optimization for the training pipeline. This includes defining a search space for learning rate, number of training epochs, weight decay, and warmup steps. The changes also refactor the configuration file to group quantization and LoRA parameters, enabling the use of soft fine-tuning (SFT) and disabling GRPO by default. The huggingface and wandb tokens are now loaded from system env variables.
Improves the Optuna optimization process by allowing configuration of the number of trials directly from the config file, switching to Hyperband pruning, and adjusting the search space for `num_train_epochs` to allow fractional values. Also, fixes an issue where the initialization of Hydra was causing problems.
Updates the model configuration to use the Gemma 3-4b-it model. This includes changes to the model name, enabling bf16, disabling gradient checkpointing, and adjusting training parameters such as gradient accumulation steps and number of training epochs. Additionally, it disables optuna optimization and configures flash_attention_2. Also updates paths to datasets. Also, the transformers dependency version is upgraded.
Adds support for the Gemma model architecture and enables gradient checkpointing for memory efficiency during training. It also switches attention implementation to "eager" and configures Weights & Biases integration.
Migrates from Flake8 to Ruff for linting and formatting. This change simplifies the linting configuration by consolidating it into `pyproject.toml` and leverages Ruff's capabilities for both linting and formatting. It removes the old Flake8 configuration and updates the pre-commit configuration accordingly.
Addresses several issues to enhance code quality: - Improves code formatting and line wrapping for better readability. - Clarifies documentation and comments for increased understanding. - Updates exception handling to provide more informative error messages.
- Updates the training configuration to use bfloat16 for torch_dtype, reduces batch sizes, adjusts training steps and eval steps for efficient training. - Refactors the model loading logic to handle different Gemma model types and aligns attention implementation settings. - Updates dependencies in pyproject.toml and requirements.txt, including adding ninja and updating torch-cuda URL. - Modifies Dockerfiles to correctly set up llama.cpp with CUDA support and arranges file copying operations for consistency. - Adds flash-attention to git ignore.
Updates the default model configuration to use a different model and adjusts training parameters. Introduces a new test suite for the training pipeline, covering key functions such as data preparation, tokenization, model conversion, and quantization. The tests use mocks and temporary directories to ensure isolation and reproducibility.
Refactors the configuration to support MLflow as a logging backend alongside Wandb. This change introduces a `logging` section in the config, allowing users to specify the logging backend and its respective configurations (e.g., MLflow experiment name, Wandb project). It also adds dependencies required for MLflow logging.
- Add comprehensive RKLLM conversion for Rockchip NPU inference - Implement configurable RKLLM parameters (target platform, quantization) - Integrate RKLLM conversion into training pipeline alongside GGUF - Add DPO (Direct Preference Optimization) training method - Enhance GRPO training with better prompt engineering and validation - Fix Optuna integration to use actual training results - Refactor training pipeline to reduce code complexity - Update configuration with RKLLM and training method options 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Separate model evaluation from unit/integration testing - Move evaluation functions to dedicated evaluation/ directory - Reorganize tests into tests/unit/ and tests/integration/ structure - Add comprehensive pytest configuration in pyproject.toml - Maintain backward compatibility through testing_model/__init__.py - Fix circular import by relocating get_user_prompt function - Clean up obsolete files and directories - Update Fire CLI integration in main.py 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Comprehensive index.rst with project overview and feature highlights - Updated training_model.rst covering all new modules and Fire CLI integration - New evaluation.rst replacing outdated testing_model.rst to match restructured evaluation framework - Enhanced hydra_config.rst with DPO/GRPO training methods and conversion options - New fire_cli.rst documenting the complete Fire CLI interface - New training_methods.rst with detailed DPO/GRPO training documentation - New rkllm_conversion.rst for Rockchip NPU deployment workflow - New docker_deployment.rst with Ollama integration and containerization - Complete api_reference.rst documenting all modules, classes, and functions - Professional conf.py with enhanced Sphinx configuration and multiple output formats
- Add environment.use_dotenv config option for Docker/Linux compatibility - Move model classes from deepeval_integration to testing_model.models - Update auth_utils to use conditional environment loading - Restructure imports and update documentation accordingly 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
…ning methods - Add new training_model/logging_utils.py with centralized logging utilities: * get_report_to_backend() for dynamic backend selection based on config * validate_mlflow_connection() for connection health checks * log_training_config(), log_training_artifacts(), log_evaluation_metrics() - Fix SFT training to use dynamic backend selection instead of hardcoded values - Fix GRPO training to use dynamic backend selection instead of hardcoded values - Fix DPO training to use dynamic backend selection instead of hardcoded values - Update documentation: * Enhanced API reference with comprehensive module documentation * Updated Hydra configuration guide with MLflow integration details * Added evaluation framework documentation - Update dependencies: * Add mlflow>=2.0.0 for experiment tracking * Update documentation requirements This resolves the issue where MLflow was initialized but bypassed by hardcoded report_to parameters in training configurations. All training methods now properly respect the configured logging backend (wandb/mlflow/none). 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Update repository information in docs/conf.py with correct GitHub details - Remove myst_parser extension to resolve build conflicts - Fix viewcode configuration for proper documentation generation - Add sphinx-rtd-theme dependency for consistent theming - Update .gitignore with tool-specific directories Enables successful HTML documentation build and resolves configuration errors.
- Run pre-commit hooks and auto-formatters (ruff --fix, black, codespell)
- Apply small, low-risk manual fixes to satisfy linters:
- `dev.py`: guard PyTorch/CUDA checks, fix indentation and logging
- `evaluation/*`: resolve ruff issues (lazy-import handling, shorten exception messages,
annotate kwargs/llm callable, helper raise abstraction)
- Remove invalid generated file `ruff_one_file_train.json` that caused check-json failures
- Add documentation note in `pyproject.toml` listing candidate strict ruff rules for team review
All changes are formatting/linting and small safety fixes; no behavioral refactors were performed.
Dockerfile: install PyYAML early, run poetry lock as a fallback and make poetry install non-interactive to avoid build-time failures when pyproject changed. docker-compose: fix rkllm_converter build context, add temporary conversion volumes and profile to enable containerized RKLLM conversion. Add RKLLM conversion tooling: add Dockerfile.rkllm, entrypoint.sh and rkllm_converting.py; provide container entrypoint and conversion CLI to convert models to RKNN/RKLLM. run_pipeline.sh: add RKLLM conversion step that reads config.yaml, builds conversion command, runs the rkllm_converter container, and preserves the Ollama upload/integration test flow. training_model/one_file_train.py: fix package import (use relative import for auth_utils) to resolve ModuleNotFoundError in container. improve RKLLM conversion logic: build rkllm container from rkllm_files, auto-detect model format (GGUF/HuggingFace), pass max_context and model-format flags, and use env secret for dataset upload auth. pyproject.toml: convert to Poetry-style metadata, add Python constraint, and deduplicate/normalize [tool.ruff] per-file-ignores to fix TOML duplicate-key errors so poetry install succeeds in CI/Docker. vk_bot: small typing/signature cleanups for clarity. This commit unblocks the Docker build and training pipeline, adds a reproducible RKLLM conversion flow, and fixes TOML/linter issues that were causing build-time failure
Implements data source modes ("auto_split", "separate_validation", "separate_files")
for more flexible and streamlined data handling,
eliminating code duplication.
Many minorfixes
Configures flash attention.
Fix logging issue
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
add data, change parameters, rename auth_utils → utils, fix circular import
fix cache for grpo
fix edge case with tokenizer
Fix prompt sending to the deepeval
|
Warning Review limit reached
More reviews will be available in 42 minutes and 34 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (6)
📝 WalkthroughWalkthroughComprehensive refactor adding DPO/GRPO training modes, DeepEval evaluation framework, Docker/RKLLM deployment tooling, centralized configuration management with validation, extensive documentation, and development utilities. Removes legacy testing modules and datasets while reorganizing evaluation/testing around the new evaluation package. ChangesCore Training, Evaluation & Configuration
Infrastructure & Deployment
Testing, Tools & Documentation
Sequence Diagram(s)sequenceDiagram
participant User
participant CLI as main.py<br/>(Fire CLI)
participant LLMLoRaCLI
participant ConfigVal as config_validation
participant Hydra
participant TrainMain as training_model/__main__
participant DPOGRPOTrain as dpo_train/grpo_train
participant Eval as evaluation
participant MLflow
User->>CLI: python main.py train --config_path conf model=llama-7b
CLI->>LLMLoRaCLI: Fire(LLMLoRaCLI).train()
LLMLoRaCLI->>Hydra: initialize_config_dir + compose(overrides)
Hydra-->>LLMLoRaCLI: cfg (DictConfig)
LLMLoRaCLI->>ConfigVal: validate_complete_config(cfg)
ConfigVal->>ConfigVal: check model/training/paths/dpo/grpo/logging
ConfigVal-->>LLMLoRaCLI: bool (pass/fail + summary)
LLMLoRaCLI->>TrainMain: main_train(data_dir, cfg)
alt cfg.optuna.enabled
TrainMain->>TrainMain: optuna_optimize(data_dir, cfg)
else standard training
TrainMain->>TrainMain: resolve data_dir, ensure paths exist
TrainMain->>DPOGRPOTrain: dpo_train or grpo_train
DPOGRPOTrain->>DPOGRPOTrain: validate config, prepare datasets
DPOGRPOTrain->>MLflow: log_training_config, log_dataset_samples
DPOGRPOTrain->>DPOGRPOTrain: configure trainer with PEFT/optimizer
DPOGRPOTrain->>DPOGRPOTrain: train with memory cleanup hooks
DPOGRPOTrain->>MLflow: log_training_artifacts, log evaluation metrics
end
alt cfg.testing.enabled
TrainMain->>Eval: test_llm or run_all_deepeval_metrics
Eval->>Eval: load dataset, instantiate evaluation models
Eval->>Eval: run async/sync evaluation tests
Eval-->>Eval: aggregate metrics per function
end
TrainMain-->>LLMLoRaCLI: results
LLMLoRaCLI-->>User: exit code
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Rationale:
Possibly related PRs
✨ Finishing Touches🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Pull Request Overview
This is a comprehensive framework redesign introducing multiple training methods (SFT, DPO, GRPO), improved architecture, memory optimization utilities, and enhanced evaluation capabilities with Fire CLI integration.
Key Changes
- Added DPO and GRPO training implementations with multiple reward functions
- Introduced Fire CLI for modern command-line interface
- Implemented memory management utilities and MLflow logging integration
- Created comprehensive test suite and custom exception hierarchy
Reviewed Changes
Copilot reviewed 73 out of 90 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| training_model/memory_utils.py | New memory management utilities with CUDA error handling |
| training_model/logging_utils.py | MLflow integration and dataset logging utilities |
| training_model/logging_config.py | Enhanced logging with named loggers and structured output |
| training_model/grpo_train.py | GRPO training implementation with reward functions |
| training_model/exceptions.py | Custom exception hierarchy for better error handling |
| training_model/dpo_train.py | DPO training implementation |
| training_model/data_preparation.py | Data processing utilities for different formats |
| training_model/config_validation.py | Comprehensive configuration validation framework |
| training_model/main.py | Updated entry point with Optuna integration |
| training_model/init.py | Fire CLI interface and expanded exports |
| tests/ | Comprehensive unit and integration test suite |
| pyproject.toml | Updated dependencies and tooling configuration |
| main.py | Fire CLI integration as primary entry point |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
add message for raise Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 36
🧹 Nitpick comments (23)
training_model/exceptions.py (1)
17-95: Excellent exception coverage with clear documentation.The specialized exception classes cover all major failure domains in the framework, and the docstrings provide clear guidance on when each exception should be raised. The hierarchy enables both specific error catching (
ConfigurationError) and general framework error handling (LLMLoRaError).Optional enhancement for future consideration:
If you find yourself needing to capture additional error context (error codes, original exceptions, configuration values, etc.), consider adding custom
__init__methods to store context as attributes:class ConfigurationError(LLMLoRaError): """Raised when configuration validation or loading fails.""" def __init__(self, message, config_key=None, config_value=None): super().__init__(message) self.config_key = config_key self.config_value = config_valueThis can improve debugging and error reporting, but it's not necessary at this stage.
docs/requirements.txt (1)
1-4: Consider pinning package versions for reproducible documentation builds.None of the documentation dependencies specify version constraints, which could lead to non-reproducible builds or breakage if packages introduce breaking changes.
Consider applying version pinning:
-sphinx -sphinx_rtd_theme -autodocsumm -myst-parser +sphinx>=7.0,<8.0 +sphinx_rtd_theme>=2.0,<3.0 +autodocsumm>=0.2,<0.3 +myst-parser>=2.0,<3.0.dvcignore (1)
1-3: Consider adding ignore patterns to improve DVC performance.The file currently contains only documentation links. Adding common patterns (e.g.,
*.pyc,__pycache__,.git/) could improve DVC performance as suggested in the comments.Example patterns to consider:
# Python __pycache__/ *.py[cod] *.egg-info/ # IDE .vscode/ .idea/ # OS .DS_Store Thumbs.db.gitignore (1)
185-185: Remove duplicate data directory ignore pattern.The pattern
data/at line 185 and/dataat line 204 both ignore data directories. The first patterndata/will match data directories at any level, making the second pattern redundant.Apply this diff to remove the duplicate:
-/dataAlso applies to: 204-204
tools/dataset/find_threat_question_problems.py (2)
33-33: Remove unusedquestion_indicatorsvariable.The
question_indicatorslist is defined but never used in the script.Apply this diff:
-# Question patterns in user input -question_indicators = ["?", "ты", "можешь", "будешь", "собираешься"] -
60-60: Move import to top of file.PEP 8 requires imports at the top of the file. The
defaultdictimport should be moved to line 6.Apply this diff:
import json +from collections import defaultdict from pathlib import PathAnd remove the import from line 60:
# Group by type -from collections import defaultdict - by_type = defaultdict(list)tools/dataset/find_question_action_problems.py (1)
39-39: Move import to top of file.PEP 8 requires imports at the top of the file.
Apply this diff:
import json +from collections import defaultdict from pathlib import PathAnd remove the import from line 39:
-from collections import defaultdict - by_action = defaultdict(list)tools/dataset/find_future_tense_problems.py (1)
52-52: Move import to top of file.PEP 8 requires imports at the top of the file.
Apply this diff:
import json +from collections import defaultdict from pathlib import PathAnd remove from line 52:
-from collections import defaultdict - by_action = defaultdict(list)tools/dataset/analyze_dataset.py (1)
59-68: Rename unused loop variable to indicate it's intentionally unused.The
keyvariable in the loop is not used within the body.Apply this diff:
-for key, example in examples.items(): +for _key, example in examples.items(): prompt_data = example.get("prompt", {})tools/dataset/validate_extended_dataset.py (1)
1-136: Consider adding CLI argument support for flexibility.The script would be more flexible with command-line arguments to specify the dataset path and validation thresholds.
Consider adding argparse support:
import argparse def main(): parser = argparse.ArgumentParser(description="Validate extended dataset") parser.add_argument( "--dataset", type=Path, default=Path(__file__).parent.parent.parent / "data" / "dataset_ru_extended.json", help="Path to dataset file" ) args = parser.parse_args() data_path = args.dataset # ... rest of validation logic if __name__ == "__main__": main()training_model/data_preparation.py (1)
48-52: Consider moving imports to module level or using a strategy pattern.The conditional import reduces code clarity and can make dependencies harder to track.
Consider one of these approaches:
Option 1: Import at module level with alias
from typing import Any from evaluation.model_evaluation import get_user_prompt as get_game_user_prompt def dataset_to_json( dataset: dict[str, Any], filename: str | Path, method: str = "classic", ) -> list[dict[str, str]]: # ... existing code ... if method == "game": user_message = get_game_user_prompt(example.get("prompt", {})) else: user_message = get_classic_user_prompt(instruction)Option 2: Strategy pattern for better extensibility
def _get_classic_messages(example): instruction = str(example.get("instruction", "")) user_message = get_classic_user_prompt(instruction) bot_message = str(example.get("output", "")) return user_message, bot_message def _get_game_messages(example): from evaluation.model_evaluation import get_user_prompt as get_game_user_prompt user_message = get_game_user_prompt(example.get("prompt", {})) bot_message = str(example.get("answer", "")) return user_message, bot_message METHODS = { "classic": _get_classic_messages, "game": _get_game_messages, }dev.py (1)
38-41: Consider more specific exception handling.While broad exception handling in a dev tool is acceptable for robustness, more specific exceptions would provide better error messages.
Consider catching specific exceptions:
try: import torch -except Exception: +except (ImportError, ModuleNotFoundError): torch = NoneSimilar changes could be applied to the exception handlers at lines 377, 390, and 396 in the
show_env_info()function.evaluation/model_evaluation.py (6)
31-61: Consider refactoring to eliminate circular import.The comment indicates this function was moved here to avoid a circular import. While the current solution works, circular dependencies often point to architectural issues that could be resolved through better module organization.
Consider extracting shared prompt utilities to a dedicated module (e.g.,
evaluation.prompt_utilsorcommon.prompts) that bothevaluationandtraining_modelcan import without creating cycles.
101-119: Simplify file writing logic.The function opens the file twice (once to clear it, once to append). This can be simplified to a single write operation.
- with Path(filename).open("w", encoding="utf-8") as file: - file.write("") - for row in dataset: system_message = system user_message = get_user_prompt(dataset[row]["prompt"]) - # user_message = str(dataset[row]['prompt']) bot_message = str(dataset[row]["answer"]) json_object = { "system": system_message, "user": user_message, "bot": bot_message, } json_objects.append(json_object) with Path(filename).open("w", encoding="utf-8") as file: - file.write(json.dumps(json_objects, indent=4, ensure_ascii=False)) + json.dump(json_objects, file, indent=4, ensure_ascii=False)
557-559: Remove redundant exception object from logging call.When using
logger.exception(), the exception is automatically included in the log. Passing it explicitly is redundant.except Exception as e: - logger.exception("Error processing test case %d: %s", number, e) + logger.exception("Error processing test case %d", number)
610-619: Clean up code quality issues.Two minor issues:
- Line 612: Unused
noqadirective for SIM113 (not enabled in your linter config)- Line 618: Redundant exception object in
logger.exceptioncallfor future in as_completed(futures): - completed += 1 # noqa: SIM113 + completed += 1 if completed % 10 == 0 or completed == total_tests: logger.info(f"Progress: {completed}/{total_tests} tests completed") try: future.result() except Exception as e: - logger.exception(f"Unexpected error in parallel test execution: {e}") + logger.exception("Unexpected error in parallel test execution")
825-830: Remove unused noqa directive.Line 828 has an unused
noqadirective for SIM113, which is not enabled in your linter configuration.await coro - completed += 1 # noqa: SIM113 + completed += 1 if completed % 10 == 0 or completed == total_tests:
962-964: Use logging.exception for exception logging.Lines 963-964 use
logger.error()followed bylogger.exception(). Sincelogger.exception()automatically includes the error message and traceback, thelogger.error()call is redundant.except Exception as e: - logger.error(f"✗ Test function {test.__name__} failed with error:") - logger.exception(e) + logger.exception(f"✗ Test function {test.__name__} failed with error")docs/CONTRIBUTING.md (1)
57-61: Consider matching GitHub username case for consistencyThe clone URL uses
timik232(lowercase), while the PR author isTimik232(capital T). Although GitHub URLs are case-insensitive, matching the actual username casing improves consistency.# Fork on GitHub, then clone your fork - git clone https://github.com/timik232/LLM_LoRa.git + git clone https://github.com/Timik232/LLM_LoRa.git cd LLM_LoRatests/integration/test_training_functions.py (1)
58-60: Remove unusedtokenizeparameter or prefix with underscore.The
tokenizeparameter is not used in theapply_chat_templatemock function. Either remove it or rename it to_tokenizeto indicate it's intentionally unused.Apply this diff:
- def apply_chat_template(messages: list, tokenize: bool = False) -> str: + def apply_chat_template(messages: list, _tokenize: bool = False) -> str: parts = [f"{m['role'].upper()}: {m['content']}" for m in messages] return "\n".join(parts)Based on static analysis hints
training_model/__init__.py (3)
5-5: Remove unnecessarynoqadirective.The
# noqa: F401comment is unnecessary if the F401 rule is not enabled in your linter configuration.-from typing import Optional # noqa: F401 +from typing import OptionalOr remove the import entirely if it's truly unused.
Based on static analysis hints
321-341: Prefix unused parameters with underscore in wrapper functions.Similar to the issue in
testing_model/__main__.py, these wrapper functions have unused parameters (correct_answer) that should be prefixed with underscore to indicate they're intentionally unused for signature compatibility.Example for
wrapper_unintended_answer_mention:def wrapper_unintended_answer_mention( - model_answer: str, correct_answer: str, **kwargs: dict + model_answer: str, _correct_answer: str, **kwargs: dict ) -> bool: user_input = kwargs.get("user_input", "") return test_unintended_answer_mention( - self.cfg, user_input, model_answer, correct_answer + self.cfg, user_input, model_answer, _correct_answer )Apply similar changes to the other wrapper functions on lines 329-332 and 334-341.
Based on static analysis hints
116-116: Consider extracting long error messages to constants.The long exception messages on lines 116, 160, and 405 could be extracted as module-level constants or moved to custom exception classes for better maintainability. This is a minor style improvement and not critical.
Example:
# At module level CONFIG_VALIDATION_ERROR = "Configuration validation failed" # In method if not validate_complete_config(self.cfg): logger.error(f"{CONFIG_VALIDATION_ERROR} - aborting training") raise ValueError(CONFIG_VALIDATION_ERROR)Based on static analysis hints
Also applies to: 160-160, 405-405
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
data/translate.csvis excluded by!**/*.csvpoetry.lockis excluded by!**/*.lock
📒 Files selected for processing (73)
.dvc/.gitignore(1 hunks).dvc/config(1 hunks).dvcignore(1 hunks).flake8(0 hunks).gitignore(1 hunks).pre-commit-config.yaml(1 hunks)Dockerfile(3 hunks)Dockerfile_cuda(1 hunks)Dockerfile_ollama(1 hunks)README.md(1 hunks)conf/config.yaml(1 hunks)data.dvc(1 hunks)data/daedalus_eng.json(0 hunks)data/test_eng.json(0 hunks)data/test_ru.json(0 hunks)data/values.txt(0 hunks)dev.py(1 hunks)docker-compose.yaml(1 hunks)docs/CONTRIBUTING.md(1 hunks)docs/README_RU.md(1 hunks)docs/api_reference.rst(1 hunks)docs/conf.py(2 hunks)docs/docker_deployment.rst(1 hunks)docs/evaluation.rst(1 hunks)docs/fire_cli.rst(1 hunks)docs/hydra_config.rst(7 hunks)docs/index.rst(1 hunks)docs/requirements.txt(1 hunks)docs/rkllm_conversion.rst(1 hunks)docs/testing_model.rst(0 hunks)docs/training_methods.rst(1 hunks)docs/training_model.rst(1 hunks)evaluation/__init__.py(1 hunks)evaluation/deepeval_integration.py(1 hunks)evaluation/game_evaluation.py(3 hunks)evaluation/model_evaluation.py(1 hunks)legacy/llama31_train.py(0 hunks)main.py(1 hunks)model.dvc(1 hunks)mypy.ini(0 hunks)pyproject.toml(2 hunks)requirements.txt(1 hunks)rkllm_files/Dockerfile.rkllm(1 hunks)rkllm_files/entrypoint.sh(1 hunks)rkllm_files/rkllm_converting.py(1 hunks)run_pipeline.sh(3 hunks)testing_model/__init__.py(1 hunks)testing_model/__main__.py(3 hunks)testing_model/deepeval_func.py(0 hunks)testing_model/models.py(1 hunks)testing_model/test.py(0 hunks)tests/README.md(1 hunks)tests/__init__.py(1 hunks)tests/integration/test_training_functions.py(1 hunks)tests/test_requirements.txt(1 hunks)tests/unit/test_dpo_grpo.py(1 hunks)tools/dataset/analyze_dataset.py(1 hunks)tools/dataset/analyze_game_coverage.py(1 hunks)tools/dataset/find_future_tense_problems.py(1 hunks)tools/dataset/find_history_ending_problems.py(1 hunks)tools/dataset/find_question_action_problems.py(1 hunks)tools/dataset/find_threat_question_problems.py(1 hunks)tools/dataset/validate_extended_dataset.py(1 hunks)training_model/__init__.py(1 hunks)training_model/__main__.py(3 hunks)training_model/config_validation.py(1 hunks)training_model/data_preparation.py(1 hunks)training_model/dpo_train.py(1 hunks)training_model/exceptions.py(1 hunks)training_model/grpo_train.py(5 hunks)training_model/logging_config.py(2 hunks)training_model/logging_utils.py(1 hunks)training_model/memory_utils.py(1 hunks)
💤 Files with no reviewable changes (10)
- data/daedalus_eng.json
- data/values.txt
- mypy.ini
- .flake8
- data/test_eng.json
- docs/testing_model.rst
- testing_model/test.py
- data/test_ru.json
- legacy/llama31_train.py
- testing_model/deepeval_func.py
🧰 Additional context used
🧬 Code graph analysis (16)
main.py (1)
training_model/__init__.py (1)
LLMLoRaCLI(32-546)
training_model/config_validation.py (2)
training_model/dpo_train.py (1)
validate_dpo_config(19-52)training_model/grpo_train.py (1)
validate_grpo_config(66-96)
dev.py (1)
training_model/logging_config.py (1)
format(59-70)
run_pipeline.sh (1)
training_model/__init__.py (1)
pipeline(372-424)
training_model/logging_utils.py (1)
training_model/logging_config.py (1)
log_dict(134-158)
tests/integration/test_training_functions.py (1)
training_model/one_file_train.py (8)
change_dir(169-186)convert_to_gguf(1266-1331)data_preparation(447-559)generate_and_tokenize_prompt(244-280)generate_prompt(189-216)model_merge_for_converting(562-634)quantize_model(1680-1735)tokenize(219-241)
training_model/__init__.py (7)
evaluation/model_evaluation.py (2)
dataset_to_json_for_test(86-119)test_llm(904-969)training_model/config_validation.py (2)
get_validation_summary(434-468)validate_complete_config(393-431)training_model/logging_config.py (1)
configure_logging(73-119)training_model/one_file_train.py (3)
convert_to_gguf(1266-1331)convert_to_rkllm(1334-1511)train(934-1171)training_model/optuna.py (1)
optuna_optimize(111-190)evaluation/game_evaluation.py (1)
test_actions(7-39)evaluation/deepeval_integration.py (3)
test_game_context_appropriateness(304-355)test_russian_language_quality(279-301)test_unintended_answer_mention(235-276)
training_model/grpo_train.py (5)
training_model/logging_utils.py (3)
get_report_to_backend(11-33)log_dataset_samples(263-350)mlflow_phase_run(37-78)training_model/memory_utils.py (2)
comprehensive_memory_cleanup(55-107)log_memory_usage(35-52)training_model/optimizer_factory.py (2)
create_optimizer(154-173)get_optimizer_config_updates(176-197)training_model/utils.py (1)
get_generation_config(17-92)training_model/config_validation.py (1)
validate_grpo_config(242-308)
training_model/data_preparation.py (1)
evaluation/model_evaluation.py (1)
get_user_prompt(32-61)
training_model/dpo_train.py (4)
training_model/logging_utils.py (3)
get_report_to_backend(11-33)log_dataset_samples(263-350)mlflow_phase_run(37-78)training_model/memory_utils.py (2)
comprehensive_memory_cleanup(55-107)log_memory_usage(35-52)training_model/optimizer_factory.py (2)
create_optimizer(154-173)get_optimizer_config_updates(176-197)training_model/one_file_train.py (1)
train(934-1171)
testing_model/__main__.py (5)
evaluation/game_evaluation.py (1)
test_actions(7-39)evaluation/model_evaluation.py (3)
dataset_to_json_for_test(86-119)test_llm(904-969)success_rate(297-298)training_model/logging_config.py (1)
configure_logging(73-119)evaluation/deepeval_integration.py (7)
test_game_context_appropriateness(304-355)test_game_context_appropriateness_async(436-484)test_russian_language_quality(279-301)test_russian_language_quality_async(400-433)test_unintended_answer_mention(235-276)test_unintended_answer_mention_async(358-397)_get_evaluation_model(134-162)training_model/__init__.py (1)
test(253-370)
testing_model/__init__.py (3)
evaluation/deepeval_integration.py (2)
test_from_dataset(558-619)test_mention_number_of_values(194-232)evaluation/model_evaluation.py (2)
test_llm(904-969)test_via_llamacpp(1054-1144)testing_model/models.py (3)
CustomLocalModel(12-85)CustomMistralModel(88-198)CustomOpenAIModel(201-294)
training_model/__main__.py (4)
evaluation/model_evaluation.py (2)
dataset_to_json_for_test(86-119)test_llm(904-969)training_model/logging_config.py (1)
configure_logging(73-119)training_model/one_file_train.py (1)
main_train(1934-1976)training_model/optuna.py (1)
optuna_optimize(111-190)
evaluation/model_evaluation.py (4)
evaluation/game_evaluation.py (1)
test_actions(7-39)training_model/data_preparation.py (1)
get_user_prompt(12-23)testing_model/models.py (3)
generate(53-63)generate(146-162)generate(248-263)training_model/__init__.py (1)
test(253-370)
evaluation/deepeval_integration.py (2)
testing_model/models.py (3)
CustomLocalModel(12-85)CustomMistralModel(88-198)CustomOpenAIModel(201-294)training_model/utils.py (1)
_load_environment_if_needed(95-103)
tests/unit/test_dpo_grpo.py (2)
training_model/dpo_train.py (3)
dpo_train(141-305)prepare_dpo_data(55-138)validate_dpo_config(19-52)training_model/grpo_train.py (8)
grpo_train(556-863)debug_reward_function(99-146)prepare_grpo_data(444-553)reward_function(149-233)validate_grpo_config(66-96)reward_function_json_validation(236-289)reward_function_action_extraction(292-400)reward_function_combined(403-441)
🪛 Checkov (3.2.334)
rkllm_files/Dockerfile.rkllm
[high] 19-19: Ensure that certificate validation isn't disabled with the pip '--trusted-host' option
(CKV2_DOCKER_4)
🪛 LanguageTool
docs/README_RU.md
[uncategorized] ~173-~173: Перед знаками препинания не должно быть пробела: «конфигурация:»
Context: ...е файлы: - conf/config.yaml: Основная конфигурация - conf/model/: Настройки моделей - conf/training/: Г...
(Punct_and_spaces)
[uncategorized] ~174-~174: Перед знаками препинания не должно быть пробела: «моделей:»
Context: ...конфигурация - conf/model/: Настройки моделей - conf/training/: Гиперпараметры обучения ### Пример кон...
(Punct_and_spaces)
docs/CONTRIBUTING.md
[grammar] ~262-~262: Use a hyphen to join words.
Context: ...tion Style - Docstrings: Use Google style docstrings - Comments: Explain...
(QB_NEW_EN_HYPHEN)
[uncategorized] ~443-~443: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...king changes - MINOR: New features, backwards compatible - PATCH: Bug fixes, backwards compa...
(EN_COMPOUND_ADJECTIVE_INTERNAL)
🪛 markdownlint-cli2 (0.18.1)
docs/README_RU.md
3-3: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
283-283: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
330-330: Bare URL used
(MD034, no-bare-urls)
349-349: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
358-358: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
376-376: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
389-389: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
399-399: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
415-415: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
427-427: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
444-444: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
docs/CONTRIBUTING.md
293-293: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
395-395: Bare URL used
(MD034, no-bare-urls)
464-464: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
README.md
5-5: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
203-203: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
211-211: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
346-346: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
450-450: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
459-459: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
477-477: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
490-490: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
500-500: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
516-516: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
528-528: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
545-545: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
🪛 Ruff (0.14.2)
tools/dataset/find_question_action_problems.py
47-47: Loop control variable reason not used within loop body
(B007)
training_model/config_validation.py
169-169: Do not catch blind exception: Exception
(BLE001)
181-181: Redundant exception object included in logging.exception call
(TRY401)
424-424: Redundant exception object included in logging.exception call
(TRY401)
464-464: Do not catch blind exception: Exception
(BLE001)
dev.py
1-1: Shebang is present but file is not executable
(EXE001)
40-40: Do not catch blind exception: Exception
(BLE001)
97-97: subprocess call: check for execution of untrusted input
(S603)
339-339: Abstract raise to an inner function
(TRY301)
339-339: Avoid specifying long messages outside the exception class
(TRY003)
340-340: subprocess call: check for execution of untrusted input
(S603)
351-351: Abstract raise to an inner function
(TRY301)
351-351: Avoid specifying long messages outside the exception class
(TRY003)
353-353: subprocess call: check for execution of untrusted input
(S603)
377-377: Do not catch blind exception: Exception
(BLE001)
390-390: Do not catch blind exception: Exception
(BLE001)
396-396: Do not catch blind exception: Exception
(BLE001)
training_model/memory_utils.py
139-139: Do not catch blind exception: Exception
(BLE001)
training_model/logging_utils.py
70-71: Remove exception handler; error is immediately re-raised
(TRY203)
103-103: Do not catch blind exception: Exception
(BLE001)
144-144: Do not catch blind exception: Exception
(BLE001)
188-188: Do not catch blind exception: Exception
(BLE001)
222-222: Do not catch blind exception: Exception
(BLE001)
256-256: Consider moving this statement to an else block
(TRY300)
259-259: Redundant exception object included in logging.exception call
(TRY401)
349-349: Do not catch blind exception: Exception
(BLE001)
tests/integration/test_training_functions.py
58-58: Unused function argument: tokenize
(ARG001)
rkllm_files/rkllm_converting.py
1-1: Shebang is present but file is not executable
(EXE001)
114-114: Avoid specifying long messages outside the exception class
(TRY003)
190-190: Consider moving this statement to an else block
(TRY300)
193-193: Redundant exception object included in logging.exception call
(TRY401)
training_model/logging_config.py
93-93: Avoid specifying long messages outside the exception class
(TRY003)
97-97: Avoid specifying long messages outside the exception class
(TRY003)
training_model/__init__.py
5-5: Unused noqa directive (non-enabled: F401)
Remove unused noqa directive
(RUF100)
116-116: Avoid specifying long messages outside the exception class
(TRY003)
160-160: Avoid specifying long messages outside the exception class
(TRY003)
330-330: Unused function argument: correct_answer
(ARG001)
330-330: Unused function argument: kwargs
(ARG001)
335-335: Unused function argument: correct_answer
(ARG001)
405-405: Avoid specifying long messages outside the exception class
(TRY003)
testing_model/models.py
25-25: Unused method argument: args
(ARG002)
26-26: Unused method argument: kwargs
(ARG002)
105-105: Unused method argument: args
(ARG002)
106-106: Unused method argument: kwargs
(ARG002)
219-219: Unused method argument: args
(ARG002)
220-220: Unused method argument: kwargs
(ARG002)
tools/dataset/analyze_dataset.py
59-59: Loop control variable key not used within loop body
Rename unused key to _key
(B007)
training_model/grpo_train.py
226-229: Use logging.exception instead of logging.error
Replace with exception
(TRY400)
236-236: Unused function argument: kwargs
(ARG001)
285-285: Do not catch blind exception: Exception
(BLE001)
286-286: Use logging.exception instead of logging.error
Replace with exception
(TRY400)
396-396: Do not catch blind exception: Exception
(BLE001)
397-397: Use logging.exception instead of logging.error
Replace with exception
(TRY400)
579-579: Avoid specifying long messages outside the exception class
(TRY003)
672-672: Avoid specifying long messages outside the exception class
(TRY003)
training_model/dpo_train.py
165-165: Avoid specifying long messages outside the exception class
(TRY003)
193-193: Avoid specifying long messages outside the exception class
(TRY003)
testing_model/__main__.py
87-89: Use logging.exception instead of logging.error
Replace with exception
(TRY400)
90-92: Use logging.exception instead of logging.error
Replace with exception
(TRY400)
96-96: Unused function argument: correct_answer
(ARG001)
96-96: Unused function argument: kwargs
(ARG001)
108-110: Use logging.exception instead of logging.error
Replace with exception
(TRY400)
111-113: Use logging.exception instead of logging.error
Replace with exception
(TRY400)
117-117: Unused function argument: correct_answer
(ARG001)
139-141: Use logging.exception instead of logging.error
Replace with exception
(TRY400)
142-144: Use logging.exception instead of logging.error
Replace with exception
(TRY400)
evaluation/model_evaluation.py
49-49: String contains ambiguous с (CYRILLIC SMALL LETTER ES). Did you mean c (LATIN SMALL LETTER C)?
(RUF001)
49-49: String contains ambiguous о (CYRILLIC SMALL LETTER O). Did you mean o (LATIN SMALL LETTER O)?
(RUF001)
49-49: String contains ambiguous р (CYRILLIC SMALL LETTER ER). Did you mean p (LATIN SMALL LETTER P)?
(RUF001)
54-54: String contains ambiguous Т (CYRILLIC CAPITAL LETTER TE). Did you mean T (LATIN CAPITAL LETTER T)?
(RUF001)
58-58: String contains ambiguous О (CYRILLIC CAPITAL LETTER O). Did you mean O (LATIN CAPITAL LETTER O)?
(RUF001)
58-58: String contains ambiguous е (CYRILLIC SMALL LETTER IE). Did you mean e (LATIN SMALL LETTER E)?
(RUF001)
275-275: Do not catch blind exception: Exception
(BLE001)
558-558: Redundant exception object included in logging.exception call
(TRY401)
612-612: Unused noqa directive (non-enabled: SIM113)
Remove unused noqa directive
(RUF100)
618-618: Redundant exception object included in logging.exception call
(TRY401)
699-699: Abstract raise to an inner function
(TRY301)
711-711: Do not catch blind exception: Exception
(BLE001)
734-734: Unpacked variable score is never used
Prefix it with an underscore or any other dummy variable pattern
(RUF059)
738-738: Abstract raise to an inner function
(TRY301)
751-751: Do not catch blind exception: Exception
(BLE001)
773-773: Redundant exception object included in logging.exception call
(TRY401)
828-828: Unused noqa directive (non-enabled: SIM113)
Remove unused noqa directive
(RUF100)
963-963: Use logging.exception instead of logging.error
Replace with exception
(TRY400)
964-964: Redundant exception object included in logging.exception call
(TRY401)
985-985: Docstring contains ambiguous с (CYRILLIC SMALL LETTER ES). Did you mean c (LATIN SMALL LETTER C)?
(RUF002)
992-992: Docstring contains ambiguous с (CYRILLIC SMALL LETTER ES). Did you mean c (LATIN SMALL LETTER C)?
(RUF002)
1066-1066: Docstring contains ambiguous с (CYRILLIC SMALL LETTER ES). Did you mean c (LATIN SMALL LETTER C)?
(RUF002)
1071-1071: Docstring contains ambiguous с (CYRILLIC SMALL LETTER ES). Did you mean c (LATIN SMALL LETTER C)?
(RUF002)
1114-1114: String contains ambiguous – (EN DASH). Did you mean - (HYPHEN-MINUS)?
(RUF001)
1115-1115: String contains ambiguous У (CYRILLIC CAPITAL LETTER U). Did you mean Y (LATIN CAPITAL LETTER Y)?
(RUF001)
1116-1116: String contains ambiguous с (CYRILLIC SMALL LETTER ES). Did you mean c (LATIN SMALL LETTER C)?
(RUF001)
1117-1117: String contains ambiguous с (CYRILLIC SMALL LETTER ES). Did you mean c (LATIN SMALL LETTER C)?
(RUF001)
evaluation/game_evaluation.py
7-7: Unused function argument: kwargs
(ARG001)
44-44: Avoid specifying long messages outside the exception class
(TRY003)
evaluation/deepeval_integration.py
30-30: Do not catch blind exception: Exception
(BLE001)
44-44: Do not catch blind exception: Exception
(BLE001)
71-71: Avoid specifying long messages outside the exception class
(TRY003)
95-95: Unused function argument: cfg
(ARG001)
99-99: Avoid specifying long messages outside the exception class
(TRY003)
162-162: Avoid specifying long messages outside the exception class
(TRY003)
184-184: subprocess call: check for execution of untrusted input
(S603)
219-219: String contains ambiguous а (CYRILLIC SMALL LETTER A). Did you mean a (LATIN SMALL LETTER A)?
(RUF001)
236-236: Unused function argument: expected_answer
(ARG001)
263-263: String contains ambiguous а (CYRILLIC SMALL LETTER A). Did you mean a (LATIN SMALL LETTER A)?
(RUF001)
359-359: Unused function argument: expected_answer
(ARG001)
383-383: String contains ambiguous а (CYRILLIC SMALL LETTER A). Did you mean a (LATIN SMALL LETTER A)?
(RUF001)
541-541: Do not catch blind exception: Exception
(BLE001)
544-544: Use logging.exception instead of logging.error
Replace with exception
(TRY400)
560-560: Unused function argument: test_dataset
(ARG001)
tests/unit/test_dpo_grpo.py
632-632: Unused method argument: mock_dpo_config
(ARG002)
700-700: Unused method argument: mock_grpo_config
(ARG002)
🪛 Shellcheck (0.11.0)
run_pipeline.sh
[warning] 76-76: Quote this to prevent word splitting.
(SC2046)
[warning] 77-77: Quote this to prevent word splitting.
(SC2046)
[error] 124-124: Parsing stopped here. Is this keyword correctly matched up?
(SC1089)
🔇 Additional comments (23)
training_model/exceptions.py (1)
1-14: LGTM! Well-structured exception hierarchy.The module docstring clearly explains the purpose, and the base exception class
LLMLoRaErrorprovides a solid foundation for catching all framework-specific errors. This design enables better error diagnosis and handling compared to generic exceptions.model.dvc (1)
1-6: LGTM!The DVC tracking configuration for the model directory is correctly structured with appropriate hash type and metadata.
.dvc/.gitignore (1)
1-3: LGTM!Standard DVC gitignore entries appropriately exclude local configurations and ephemeral data from version control.
evaluation/__init__.py (1)
1-1: LGTM!Standard package initialization with clear documentation.
data.dvc (1)
1-6: LGTM!The DVC tracking configuration for the data directory is correctly structured with appropriate metadata.
.dvc/config (2)
1-3: LGTM on cache configuration.The hardlink cache type with protection enabled is an appropriate choice for performance and data integrity.
6-7: Remove the suggested fix; the approach using${DVC_REMOTE_URL}environment variables in.dvc/configis not supported by DVC and will not work.The portability concern is valid — UNC paths will not work natively on Linux and require the share to be mounted to a POSIX path. However,
.dvc/configis meant to be committed (per DVC best practices for non-sensitive shared settings); the real issue is that different machines may need different URLs or mount paths.The correct solution is to move the remote configuration to
.dvc/config.local(which DVC gitignores by default), allowing each developer to configure their own URL based on their environment:dvc remote add nas-remote //diskstation_105/dvc_storage --local # or on Linux with the share mounted: dvc remote add nas-remote /mnt/dvc_storage --localEach machine then maintains its own
.dvc/config.localwithout committing it. This respects DVC's design where.dvc/configholds shared settings and.dvc/config.localholds machine-specific ones.Likely an incorrect or invalid review comment.
evaluation/game_evaluation.py (1)
1-44: LGTM! Past review feedback addressed.The refactoring successfully addresses the previous review comment about the missing error message. The new
_raise_action_mismatch()helper provides a clear, descriptive error message when actions don't match. The**kwargsparameter enables flexible integration with testing frameworks while maintaining backward compatibility.requirements.txt (1)
1-203: LGTM: Dependency updates align with CUDA 12.4 upgrade.The CUDA version update from cu118 to cu124 and associated dependency version bumps are consistent with the project's modernization goals described in the README.
pyproject.toml (1)
1-138: LGTM: Dependency and tooling updates support project modernization.The updates include:
- Broader Python version support (3.11-3.13)
- CUDA 12.4 support
- Loosened dependency constraints for flexibility
- Enhanced code quality tooling (ruff, bandit, codespell)
- Comprehensive pytest configuration
These changes align well with the comprehensive refactoring described in the PR.
conf/config.yaml (1)
1-259: LGTM: Configuration expansion supports new features.The configuration updates comprehensively support the new features mentioned in the PR:
- GRPO and DPO training methods
- RKLLM conversion for edge deployment
- Enhanced memory management
- Optuna hyperparameter optimization
- DeepEval integration
The structure is well-organized with clear comments and sensible defaults.
.pre-commit-config.yaml (1)
1-72: LGTM: Pre-commit hooks modernized and enhanced.The updates improve code quality tooling:
- Version updates for core hooks
- Flake8 replaced with more comprehensive ruff configuration
- Bandit added for security checks
- Codespell added for spelling checks
- Better hook descriptions and configurations
These changes align with the tooling updates in
pyproject.toml.tests/__init__.py (1)
1-2: LGTM!Standard package marker file for the tests directory.
docs/docker_deployment.rst (1)
1-912: Comprehensive Docker deployment documentation.This extensive documentation provides excellent coverage of Docker-based deployment workflows, including Ollama integration, security considerations, scaling strategies, and monitoring. The examples are well-structured and cover multiple deployment scenarios.
Consider these minor enhancements for future iterations:
- Add version pinning examples in the Docker Compose snippets to improve reproducibility
- Include troubleshooting section for common deployment issues
- Add links to external documentation (Docker, Ollama, Kubernetes) for readers who need additional context
main.py (1)
1-7: Clean Fire-based CLI entrypoint.The migration to Python Fire for CLI handling is a good improvement for usability. The implementation is straightforward and follows Fire's best practices.
docs/index.rst (1)
1-109: Excellent documentation structure.The index provides a clear, comprehensive overview of the framework with well-organized sections. The feature highlights, architecture overview, and structured navigation make it easy for users to find what they need.
tests/README.md (1)
1-142: Comprehensive testing guide.Excellent documentation covering test execution, structure, debugging, and contribution guidelines. The examples and fixture descriptions will help developers understand and extend the test suite effectively.
tests/test_requirements.txt (1)
1-22: Well-organized test requirements.The test requirements file is clearly structured with appropriate comments explaining the purpose of each section and noting dependencies that overlap with main requirements.
testing_model/__init__.py (1)
1-16: LGTM! Clean API reorganization.The imports and public API surface are well-structured. The module correctly exposes testing utilities and custom model classes from their new locations.
tests/integration/test_training_functions.py (1)
116-330: Excellent test coverage for training pipeline functions.The test suite comprehensively covers key training functions including directory management, prompt generation, tokenization, data preparation, model merging, and quantization. Good use of mocking to isolate dependencies.
testing_model/__main__.py (2)
31-161: Well-designed DeepEval integration with dynamic metric loading.The
_create_deepeval_test_functionshelper cleanly handles:
- Configuration-based metric selection
- Safe fallbacks when DeepEval is unavailable
- Clear logging of enabled/disabled metrics
- Proper wrapper creation for signature compatibility
228-254: Comprehensive test summary logging.The final summary provides detailed metrics per test function with clear formatting. Good use of conditional logic to handle both single and multiple test scenarios.
training_model/__init__.py (1)
32-547: Excellent CLI design with comprehensive training pipeline integration.The
LLMLoRaCLIclass provides a clean Fire-based interface for:
- Training with optional Optuna optimization
- Multi-format model conversion (GGUF/RKLLM)
- Testing with optional DeepEval metrics
- Full pipeline orchestration
The configuration handling with Hydra integration, override application, and validation checks is well-implemented.
| @@ -0,0 +1,520 @@ | |||
| #!/usr/bin/env python3 | |||
There was a problem hiding this comment.
Make file executable to match shebang.
The file has a shebang but isn't marked as executable.
Run this command to make it executable:
chmod +x dev.py🧰 Tools
🪛 Ruff (0.14.2)
1-1: Shebang is present but file is not executable
(EXE001)
🤖 Prompt for AI Agents
In dev.py around lines 1 to 1 the file contains a shebang but is not executable;
make the script executable by setting the execute bit for appropriate users
(e.g., run chmod +x dev.py in the repo or adjust file mode in CI/git so the file
is committed with executable permissions) so the shebang is honored when running
the script directly.
| - "11434:11434" | ||
| volumes: | ||
| - ollama_models:/root/.ollama | ||
| - ./models:/training_model/models | ||
| - ./models:/app/models | ||
| environment: | ||
| - OLLAMA_MODELS=/training_model/models | ||
| - OLLAMA_MODELS=/app/models | ||
| runtime: nvidia | ||
| healthcheck: | ||
| test: [ "CMD", "curl", "-f", "http://localhost:11434" ] | ||
| interval: 10s | ||
| timeout: 5s | ||
| retries: 5 | ||
|
|
||
| rkllm_converter: | ||
| build: | ||
| context: rkllm_files/ | ||
| dockerfile: Dockerfile.rkllm | ||
| image: rkllm_converter | ||
| container_name: rkllm_converter | ||
| volumes: | ||
| - ./models:/app/models | ||
| - ./data:/app/data | ||
| # Temporary volumes for conversion I/O | ||
| - /tmp/rkllm_input:/input | ||
| - /tmp/rkllm_output:/output | ||
| environment: | ||
| - PYTHONUNBUFFERED=1 | ||
| profiles: ["converter"] | ||
|
|
||
|
|
||
| llm_training: | ||
| build: | ||
| context: . | ||
| dockerfile: Dockerfile | ||
| container_name: llm_training | ||
| volumes: | ||
| - ./models:/training_model/models | ||
| - ./data:/training_model/data | ||
| - ./conf:/training_model/conf | ||
| - ./models:/app/models | ||
| - ./data:/app/data | ||
| - ./conf:/app/conf | ||
| - /var/run/docker.sock:/var/run/docker.sock | ||
| depends_on: | ||
| ollama: | ||
| condition: service_healthy | ||
| environment: | ||
| - OLLAMA_HOST=http://ollama:11434 | ||
| runtime: nvidia | ||
| networks: | ||
| - mlflow-network | ||
|
|
There was a problem hiding this comment.
Ensure llm_training can reach ollama.
llm_training is isolated on mlflow-network, while ollama stays on the default network, so DNS for ollama is unreachable and the training container can’t hit http://ollama:11434.
Attach both services to the same networks, e.g.:
ollama:
...
- volumes:
- - ollama_models:/root/.ollama
- - ./models:/app/models
+ volumes:
+ - ollama_models:/root/.ollama
+ - ./models:/app/models
environment:
- OLLAMA_MODELS=/app/models
runtime: nvidia
+ networks:
+ - default
+ - mlflow-network
...
llm_training:
...
- networks:
- - mlflow-network
+ networks:
+ - default
+ - mlflow-network📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - "11434:11434" | |
| volumes: | |
| - ollama_models:/root/.ollama | |
| - ./models:/training_model/models | |
| - ./models:/app/models | |
| environment: | |
| - OLLAMA_MODELS=/training_model/models | |
| - OLLAMA_MODELS=/app/models | |
| runtime: nvidia | |
| healthcheck: | |
| test: [ "CMD", "curl", "-f", "http://localhost:11434" ] | |
| interval: 10s | |
| timeout: 5s | |
| retries: 5 | |
| rkllm_converter: | |
| build: | |
| context: rkllm_files/ | |
| dockerfile: Dockerfile.rkllm | |
| image: rkllm_converter | |
| container_name: rkllm_converter | |
| volumes: | |
| - ./models:/app/models | |
| - ./data:/app/data | |
| # Temporary volumes for conversion I/O | |
| - /tmp/rkllm_input:/input | |
| - /tmp/rkllm_output:/output | |
| environment: | |
| - PYTHONUNBUFFERED=1 | |
| profiles: ["converter"] | |
| llm_training: | |
| build: | |
| context: . | |
| dockerfile: Dockerfile | |
| container_name: llm_training | |
| volumes: | |
| - ./models:/training_model/models | |
| - ./data:/training_model/data | |
| - ./conf:/training_model/conf | |
| - ./models:/app/models | |
| - ./data:/app/data | |
| - ./conf:/app/conf | |
| - /var/run/docker.sock:/var/run/docker.sock | |
| depends_on: | |
| ollama: | |
| condition: service_healthy | |
| environment: | |
| - OLLAMA_HOST=http://ollama:11434 | |
| runtime: nvidia | |
| networks: | |
| - mlflow-network | |
| - "11434:11434" | |
| volumes: | |
| - ollama_models:/root/.ollama | |
| - ./models:/app/models | |
| environment: | |
| - OLLAMA_MODELS=/app/models | |
| runtime: nvidia | |
| networks: | |
| - default | |
| - mlflow-network | |
| healthcheck: | |
| test: [ "CMD", "curl", "-f", "http://localhost:11434" ] | |
| interval: 10s | |
| timeout: 5s | |
| retries: 5 | |
| rkllm_converter: | |
| build: | |
| context: rkllm_files/ | |
| dockerfile: Dockerfile.rkllm | |
| image: rkllm_converter | |
| container_name: rkllm_converter | |
| volumes: | |
| - ./models:/app/models | |
| - ./data:/app/data | |
| # Temporary volumes for conversion I/O | |
| - /tmp/rkllm_input:/input | |
| - /tmp/rkllm_output:/output | |
| environment: | |
| - PYTHONUNBUFFERED=1 | |
| profiles: ["converter"] | |
| llm_training: | |
| build: | |
| context: . | |
| dockerfile: Dockerfile | |
| container_name: llm_training | |
| volumes: | |
| - ./models:/app/models | |
| - ./data:/app/data | |
| - ./conf:/app/conf | |
| - /var/run/docker.sock:/var/run/docker.sock | |
| depends_on: | |
| ollama: | |
| condition: service_healthy | |
| environment: | |
| - OLLAMA_HOST=http://ollama:11434 | |
| runtime: nvidia | |
| networks: | |
| - default | |
| - mlflow-network |
🤖 Prompt for AI Agents
docker-compose.yaml around lines 8-56: llm_training is placed on mlflow-network
while ollama remains only on the default network, so the llm_training service
cannot resolve the hostname "ollama"; modify the compose file to attach ollama
to the same network(s) as llm_training (e.g., add mlflow-network under the
ollama service networks section, or add both services to a shared network) and
ensure the mlflow-network is declared under the top-level networks section so
Docker Compose creates it.
| VOLUME ["/root/.ollama", "/training_model/models"] | ||
| RUN mkdir -p /training_model/models && chmod 777 /training_model/models | ||
| VOLUME ["/root/.ollama", "/app/models"] | ||
| RUN mkdir -p /app/models && chmod 777 /app/models |
There was a problem hiding this comment.
Overly permissive directory permissions pose a security risk.
Setting permissions to 777 grants read, write, and execute access to all users, which violates the principle of least privilege and could allow unauthorized modifications.
Apply this diff to use more restrictive permissions:
-RUN mkdir -p /app/models && chmod 777 /app/models
+RUN mkdir -p /app/models && chmod 755 /app/modelsIf write access by the Ollama process is required, consider using 775 with appropriate group ownership instead of world-writable permissions.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| RUN mkdir -p /app/models && chmod 777 /app/models | |
| RUN mkdir -p /app/models && chmod 755 /app/models |
🤖 Prompt for AI Agents
In Dockerfile_ollama around line 6, the RUN command currently sets overly
permissive 777 on /app/models; change this to create the directory, set
ownership to the user/group that the Ollama process runs as, and use restrictive
permissions (e.g. 775) so only owner/group can write while others are
read/execute. Ensure the Dockerfile uses chown to assign the directory to the
Ollama runtime user/group before or when setting permissions and avoid
world-writable mode.
| .. automethod:: main.LLMLoRAFramework.train_model | ||
| .. automethod:: main.LLMLoRAFramework.evaluate_model | ||
| .. automethod:: training_model.LLMLoRaCLI.convert | ||
| .. automethod:: training_model.LLMLoRaCLI.convert | ||
| .. automethod:: main.LLMLoRAFramework.list_models | ||
| .. automethod:: main.LLMLoRAFramework.clean_checkpoints | ||
|
|
There was a problem hiding this comment.
Remove duplicate directive and verify class references.
Two issues:
- Line 283 has a duplicate
automethoddirective forconvert - References to
main.LLMLoRAFramework(lines 280-281, 284-285) may be incorrect - should verify if the class is actuallytraining_model.LLMLoRaCLIbased on the context from other files in this PR
Remove the duplicate on line 283 and verify the class name matches the actual implementation:
.. automethod:: main.LLMLoRAFramework.train_model
.. automethod:: main.LLMLoRAFramework.evaluate_model
.. automethod:: training_model.LLMLoRaCLI.convert
-.. automethod:: training_model.LLMLoRaCLI.convert
.. automethod:: main.LLMLoRAFramework.list_models
.. automethod:: main.LLMLoRAFramework.clean_checkpoints📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| .. automethod:: main.LLMLoRAFramework.train_model | |
| .. automethod:: main.LLMLoRAFramework.evaluate_model | |
| .. automethod:: training_model.LLMLoRaCLI.convert | |
| .. automethod:: training_model.LLMLoRaCLI.convert | |
| .. automethod:: main.LLMLoRAFramework.list_models | |
| .. automethod:: main.LLMLoRAFramework.clean_checkpoints | |
| .. automethod:: main.LLMLoRAFramework.train_model | |
| .. automethod:: main.LLMLoRAFramework.evaluate_model | |
| .. automethod:: training_model.LLMLoRaCLI.convert | |
| .. automethod:: main.LLMLoRAFramework.list_models | |
| .. automethod:: main.LLMLoRAFramework.clean_checkpoints |
🤖 Prompt for AI Agents
In docs/api_reference.rst around lines 280 to 286 there is a duplicate
automethod directive for training_model.LLMLoRaCLI.convert (line 283) and
several automethods reference main.LLMLoRAFramework (lines 280-281 and 284-285)
which may be incorrect; remove the duplicated automethod entry for convert and
update the referenced class paths to the actual implementing class (likely
training_model.LLMLoRaCLI) by replacing any incorrect main.LLMLoRAFramework
prefixes with the correct module/class name after verifying the implementation
in the source files.
| * **BLEU Score**: Bilingual evaluation for text similarity | ||
| * **ROGUE Score**: Recall-oriented evaluation for summarization |
There was a problem hiding this comment.
Fix typo: "ROGUE" should be "ROUGE"
The metric name is misspelled. It should be "ROUGE Score" (Recall-Oriented Understudy for Gisting Evaluation), not "ROGUE Score".
Apply this diff:
-* **ROGUE Score**: Recall-oriented evaluation for summarization
+* **ROUGE Score**: Recall-oriented evaluation for summarization📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| * **BLEU Score**: Bilingual evaluation for text similarity | |
| * **ROGUE Score**: Recall-oriented evaluation for summarization | |
| * **BLEU Score**: Bilingual evaluation for text similarity | |
| * **ROUGE Score**: Recall-oriented evaluation for summarization |
🤖 Prompt for AI Agents
In docs/evaluation.rst around lines 95 to 96, the metric name "ROGUE Score" is
misspelled; change "ROGUE Score" to "ROUGE Score" so the line reads "* **ROUGE
Score**: Recall-oriented evaluation for summarization". Ensure only the spelling
is corrected and capitalization remains consistent with the surrounding list.
| def get_user_prompt(data: str) -> str: | ||
| """ | ||
| Construct a user prompt from conversation data. | ||
|
|
||
| Args: | ||
| data (str): conversation data | ||
|
|
||
| Returns: | ||
| str: Formatted prompt string with conversation context. | ||
| """ | ||
| prompt = f"Ответь на вопрос пользователя коротко: {data}" | ||
| return prompt |
There was a problem hiding this comment.
Rename function to avoid confusion with evaluation.model_evaluation.get_user_prompt.
This module's get_user_prompt(data: str) has the same name but a different signature from evaluation.model_evaluation.get_user_prompt(data: dict[str, Any]). This creates confusion and makes the conditional import at line 50 necessary.
Apply this diff to use a more descriptive name:
-def get_user_prompt(data: str) -> str:
+def get_classic_user_prompt(data: str) -> str:
"""
Construct a user prompt from conversation data.
Args:
data (str): conversation data
Returns:
str: Formatted prompt string with conversation context.
"""
prompt = f"Ответь на вопрос пользователя коротко: {data}"
return promptThen update the call at line 70:
else:
# Classic method: simple instruction/output format
instruction = str(example.get("instruction", ""))
- user_message = get_user_prompt(instruction)
+ user_message = get_classic_user_prompt(instruction)
bot_message = str(example.get("output", ""))🤖 Prompt for AI Agents
In training_model/data_preparation.py around lines 12 to 23, the function
get_user_prompt(data: str) conflicts with
evaluation.model_evaluation.get_user_prompt (different signature) and causes the
conditional import; rename this function to a more descriptive name (e.g.,
build_user_prompt_from_text or format_user_prompt_text) and update any internal
references accordingly; then update the call at line 70 to use the new name so
the module no longer collides with evaluation.model_evaluation.get_user_prompt.
| def do_transform() -> None: | ||
| """One run function to convert the dataset.""" | ||
| import logging | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
| base_dir = Path(__file__).resolve().parent.parent | ||
|
|
||
| input_file_path = base_dir / "data" / "intents_dataset.json" | ||
| with input_file_path.open("r", encoding="utf-8") as f: | ||
| input_json = json.load(f) | ||
|
|
||
| result = transform_topics(input_json) | ||
|
|
||
| output_file_path = base_dir / "data" / "intent_responses.json" | ||
| with output_file_path.open("w", encoding="utf-8") as f: | ||
| json.dump(result, f, ensure_ascii=False, indent=4) | ||
|
|
||
| logger.info( | ||
| "Dataset successfully converted to new format and saved to %s", | ||
| output_file_path, | ||
| ) | ||
|
|
There was a problem hiding this comment.
Remove duplicate logging import.
logging is already imported at line 6, no need to import it again in the function.
Apply this diff:
def do_transform() -> None:
"""One run function to convert the dataset."""
- import logging
-
logger = logging.getLogger(__name__)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| def do_transform() -> None: | |
| """One run function to convert the dataset.""" | |
| import logging | |
| logger = logging.getLogger(__name__) | |
| base_dir = Path(__file__).resolve().parent.parent | |
| input_file_path = base_dir / "data" / "intents_dataset.json" | |
| with input_file_path.open("r", encoding="utf-8") as f: | |
| input_json = json.load(f) | |
| result = transform_topics(input_json) | |
| output_file_path = base_dir / "data" / "intent_responses.json" | |
| with output_file_path.open("w", encoding="utf-8") as f: | |
| json.dump(result, f, ensure_ascii=False, indent=4) | |
| logger.info( | |
| "Dataset successfully converted to new format and saved to %s", | |
| output_file_path, | |
| ) | |
| def do_transform() -> None: | |
| """One run function to convert the dataset.""" | |
| logger = logging.getLogger(__name__) | |
| base_dir = Path(__file__).resolve().parent.parent | |
| input_file_path = base_dir / "data" / "intents_dataset.json" | |
| with input_file_path.open("r", encoding="utf-8") as f: | |
| input_json = json.load(f) | |
| result = transform_topics(input_json) | |
| output_file_path = base_dir / "data" / "intent_responses.json" | |
| with output_file_path.open("w", encoding="utf-8") as f: | |
| json.dump(result, f, ensure_ascii=False, indent=4) | |
| logger.info( | |
| "Dataset successfully converted to new format and saved to %s", | |
| output_file_path, | |
| ) |
🤖 Prompt for AI Agents
In training_model/data_preparation.py around lines 113 to 135 there is a
redundant "import logging" inside do_transform(); remove that inner import so
the function uses the module-level logging import (already present at the top of
the file) and keep the logger = logging.getLogger(__name__) and remaining code
unchanged.
| import json | ||
| import logging | ||
| from collections.abc import Callable | ||
| from pathlib import Path | ||
|
|
||
| from datasets import Dataset | ||
| from omegaconf import DictConfig | ||
| from peft import LoraConfig, PeftModel | ||
| from transformers import AutoModel, AutoTokenizer, PreTrainedModel, PreTrainedTokenizer | ||
| from trl import DPOConfig, DPOTrainer | ||
|
|
||
| from .logging_utils import get_report_to_backend, log_dataset_samples, mlflow_phase_run | ||
| from .memory_utils import comprehensive_memory_cleanup, log_memory_usage | ||
| from .optimizer_factory import create_optimizer, get_optimizer_config_updates | ||
|
|
||
|
|
||
| def validate_dpo_config(cfg: DictConfig) -> bool: | ||
| """Validate DPO configuration parameters. | ||
|
|
||
| Args: | ||
| cfg: Configuration object | ||
|
|
||
| Returns: | ||
| bool: True if configuration is valid | ||
| """ | ||
| if not hasattr(cfg, "dpo"): | ||
| logging.error("Missing 'dpo' section in config") | ||
| return False | ||
| required_dpo_params = ["val_data", "train_data"] | ||
| missing_params = [p for p in required_dpo_params if not hasattr(cfg.dpo, p)] | ||
|
|
||
| if missing_params: | ||
| logging.error(f"Missing required DPO parameters: {missing_params}") | ||
| return False | ||
|
|
||
| # Validate data files exist | ||
| data_dir = Path.cwd() / cfg.paths.data_dir | ||
| train_file = data_dir / cfg.dpo.train_data | ||
| val_file = data_dir / cfg.dpo.val_data | ||
|
|
||
| if not train_file.exists(): | ||
| logging.error(f"DPO training data file not found: {train_file}") | ||
| return False | ||
|
|
||
| if not val_file.exists(): | ||
| logging.error(f"DPO validation data file not found: {val_file}") | ||
| return False | ||
|
|
||
| logging.info("DPO configuration validation passed") | ||
| return True | ||
|
|
||
|
|
||
| def prepare_dpo_data(cfg: DictConfig) -> tuple[Dataset, Dataset]: | ||
| """Prepare datasets for DPO training with preference pairs. | ||
|
|
||
| Args: | ||
| cfg (DictConfig): Configuration object | ||
|
|
||
| Returns: | ||
| Tuple[Dataset, Dataset]: Tuple containing train and validation datasets | ||
| """ | ||
| data_dir = Path.cwd() / cfg.paths.data_dir | ||
|
|
||
| with (data_dir / cfg.dpo.val_data).open(encoding="utf-8") as file: | ||
| test_dataset = json.load(file) | ||
|
|
||
| with (data_dir / cfg.dpo.train_data).open(encoding="utf-8") as file: | ||
| train_dataset = json.load(file) | ||
|
|
There was a problem hiding this comment.
Fix Hydra data-directory resolution.
Hydra changes cwd to the run output directory, so joining cfg.paths.data_dir with Path.cwd() looks under outputs/.../data and makes validation/preparation fail even though the dataset exists at the project root. This breaks normal Hydra runs and the new tests. Please resolve the base directory via hydra.utils.get_original_cwd() (with a safe fallback) before computing data_dir.
-from pathlib import Path
+from pathlib import Path
+
+from hydra.utils import get_original_cwd
@@
- data_dir = Path.cwd() / cfg.paths.data_dir
+ try:
+ data_root = Path(get_original_cwd())
+ except ValueError:
+ data_root = Path.cwd()
+ data_dir = data_root / cfg.paths.data_dir
@@
- data_dir = Path.cwd() / cfg.paths.data_dir
+ try:
+ data_root = Path(get_original_cwd())
+ except ValueError:
+ data_root = Path.cwd()
+ data_dir = data_root / cfg.paths.data_dir🤖 Prompt for AI Agents
In training_model/dpo_train.py around lines 3 to 71, Hydra changes cwd to the
run output dir so using Path.cwd() to resolve cfg.paths.data_dir is wrong;
replace Path.cwd() with hydra.utils.get_original_cwd() (import it at the top)
and fall back to Path.cwd() if get_original_cwd() is not available or returns
None/empty; apply this change where data_dir is computed both in
validate_dpo_config and prepare_dpo_data so data files are resolved relative to
the project root rather than the Hydra output dir.
| required_grpo_params = ["val_data", "train_data", "num_generations"] | ||
| missing_params = [param for param in required_grpo_params if not hasattr(cfg.grpo, param)] | ||
|
|
||
| if missing_params: | ||
| logging.error(f"Missing required GRPO parameters: {missing_params}") | ||
| return False | ||
|
|
||
| # Validate data files exist | ||
| data_dir = Path.cwd() / cfg.paths.data_dir | ||
| train_file = data_dir / cfg.grpo.train_data | ||
| val_file = data_dir / cfg.grpo.val_data | ||
|
|
||
| if not train_file.exists(): | ||
| logging.error(f"GRPO training data file not found: {train_file}") | ||
| return False | ||
|
|
||
| if not val_file.exists(): | ||
| logging.error(f"GRPO validation data file not found: {val_file}") | ||
| return False | ||
|
|
||
| logging.info("GRPO configuration validation passed") |
There was a problem hiding this comment.
Defend against missing GRPO config sections before touching them.
This reimplementation assumes cfg.grpo and cfg.paths already exist. When either section is omitted or optional in the Hydra config, validate_grpo_config(cfg) now raises AttributeError instead of producing a controlled error message, which blocks GRPO outright. The previous shared validator in training_model/config_validation.py (lines 241-307 in the provided snippet) handled these edge cases gracefully—let’s bring those guards back locally or, better yet, reuse the shared helper.
Please add explicit checks before dereferencing these sections, e.g.:
+ if not hasattr(cfg, "grpo") or cfg.grpo is None:
+ logging.error("GRPO configuration not found - aborting validation")
+ return False
+
+ if not hasattr(cfg, "paths") or cfg.paths is None:
+ logging.error("paths section is required for GRPO data files")
+ return False
+
+ data_dir_value = getattr(cfg.paths, "data_dir", None)
+ if data_dir_value is None:
+ logging.error("paths.data_dir must be defined for GRPO")
+ return Falseand then proceed to build the dataset paths with data_dir_value.
🤖 Prompt for AI Agents
In training_model/grpo_train.py around lines 75 to 95, the validator
dereferences cfg.grpo and cfg.paths without checking they exist which causes
AttributeError when those sections are omitted; add guards to detect missing
sections (e.g., if not hasattr(cfg, "grpo") or not hasattr(cfg, "paths") then
log a clear error and return False), or preferably import and call the shared
validator from training_model/config_validation.py to reuse its existing checks;
after confirming cfg.paths and cfg.grpo exist, read data_dir_value =
getattr(cfg.paths, "data_dir") (or similar) and construct train_file/val_file
from that value before checking .exists(), keeping the same error logs and
return False behaviour on missing params or files.
| temperature=getattr(generation_config, "temperature", 1.0), | ||
| top_p=getattr(generation_config, "top_p", 1.0), | ||
| top_k=getattr(generation_config, "top_k", 50), |
There was a problem hiding this comment.
Read generation_config like a dict, otherwise overrides never apply.
get_generation_config (see training_model/utils.py) returns a plain dict. Using getattr(generation_config, "temperature", …) always misses and falls back to the default literal, so any GRPO-specific overrides in the config file are ignored during training. This regresses the configurability of GRPOConfig.
Switch these lookups to dict.get so that user-specified values make it into the trainer:
- temperature=getattr(generation_config, "temperature", 1.0),
- top_p=getattr(generation_config, "top_p", 1.0),
- top_k=getattr(generation_config, "top_k", 50),
+ temperature=generation_config.get("temperature", 1.0),
+ top_p=generation_config.get("top_p", 1.0),
+ top_k=generation_config.get("top_k", 50),This keeps GRPO aligned with the auxiliary generation settings surfaced in the new config flow and the TRL GRPOConfig API. (huggingface.co)
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| temperature=getattr(generation_config, "temperature", 1.0), | |
| top_p=getattr(generation_config, "top_p", 1.0), | |
| top_k=getattr(generation_config, "top_k", 50), | |
| temperature=generation_config.get("temperature", 1.0), | |
| top_p=generation_config.get("top_p", 1.0), | |
| top_k=generation_config.get("top_k", 50), |
🤖 Prompt for AI Agents
In training_model/grpo_train.py around lines 758 to 760, the code uses
getattr(generation_config, "...", ...) but generation_config is a plain dict
from get_generation_config, so overrides are ignored; replace those getattr
calls with generation_config.get("temperature", 1.0),
generation_config.get("top_p", 1.0), and generation_config.get("top_k", 50)
respectively so user-provided values are respected while keeping the same
default fallbacks.
There was a problem hiding this comment.
Actionable comments posted: 15
♻️ Duplicate comments (30)
docs/fire_cli.rst (2)
213-215: Inconsistent command names in pipeline example.The pipeline example uses
trainandtestinstead of the documentedtrain_modelandevaluate_modelcommands (lines 24-72).
262-280: Inconsistent command names in workflow examples.The development and production workflow examples use shortened command names (
train,test) instead of the documented full names (train_model,evaluate_model).tools/dataset/find_history_ending_problems.py (1)
10-12: Replace hardcoded Windows path with configurable path.This issue was already flagged in a previous review. The hardcoded Windows-specific path prevents cross-platform usage.
tools/dataset/find_threat_question_problems.py (1)
9-11: Replace hardcoded Windows path with configurable path.This issue was already flagged in a previous review.
tools/dataset/find_question_action_problems.py (1)
9-11: Replace hardcoded Windows path with configurable path.This issue was already flagged in a previous review.
tools/dataset/find_future_tense_problems.py (1)
9-11: Replace hardcoded Windows path with configurable path.This issue was already flagged in a previous review.
tools/dataset/analyze_dataset.py (1)
5-7: Replace hardcoded Windows path with configurable path.This issue was already flagged in a previous review.
tools/dataset/analyze_game_coverage.py (1)
4-6: Replace hardcoded Windows path with configurable path.This issue was already flagged in a previous review.
README.md (1)
346-381: Add language identifier to fenced code block.This issue was already flagged in a previous review. The project structure code block should specify a language (e.g.,
text) for proper rendering.tools/dataset/validate_extended_dataset.py (1)
6-13: Fix hardcoded path and add robust file handlingThe script still hardcodes a Windows-only absolute path and exits without guarding common I/O failures, so it breaks immediately in Docker/CI or on non-Windows hosts. Please resolve the path relative to the repo (or from an env var) and wrap the open/parse step in targeted error handling.
-import json -from collections import Counter -from pathlib import Path +import json +import os +import sys +from collections import Counter +from pathlib import Path @@ -# Load extended dataset -data_path = Path("T:/projects/LLM_LoRa/LLM_LoRa/data/dataset_ru_extended.json") -with open(data_path, encoding="utf-8") as f: - data = json.load(f) +# Load extended dataset +data_path = Path( + os.environ.get( + "EXTENDED_DATASET_PATH", + Path(__file__).resolve().parents[2] / "data" / "dataset_ru_extended.json", + ) +) + +try: + with data_path.open(encoding="utf-8") as f: + data = json.load(f) +except FileNotFoundError: + print(f"Error: Dataset not found at {data_path}") + sys.exit(1) +except PermissionError: + print(f"Error: Permission denied reading {data_path}") + sys.exit(1) +except json.JSONDecodeError as exc: + print(f"Error: Invalid JSON in dataset: {exc}") + sys.exit(1)rkllm_files/rkllm_converting.py (1)
25-27: Guardtorch.cuda.empty_cache()for CPU-only environmentsCalling
torch.cuda.empty_cache()before checking CUDA availability still triggersAssertionError: Torch not compiled with CUDA enabledon CPU-only installs, preventing the converter from even parsing CLI arguments.(reddit.com) Add a guard (or swallow the assertion) so the tool remains usable without CUDA.os.environ["PYTORCH_CUDA_ALLOC_CONF"] = "expandable_segments:True" -torch.cuda.empty_cache() +if torch.cuda.is_available(): + torch.cuda.empty_cache()evaluation/model_evaluation.py (3)
66-66: Remove import-time mutation ofollama.base_url.Setting the Ollama client base URL at import time mutates global state for every consumer of this module and makes it impossible to use different endpoints in the same process (e.g., parallel tests or multi-tenant services). Configure the URL when you create or receive the client instead (inside
test_llmor before callingollama_generate) and drop the module-level assignment.
167-170: Provide a meaningful error message when the LLM returns no choices.Raising a bare
RuntimeError()(and suppressing the exception chain withfrom None) gives zero context whenresponse.choicesis empty, making on-call debugging painful. Include a descriptive message and keep the original traceback. This mirrors the earlier review feedback.
711-752: Stop swallowing unexpected exceptions in async test execution.Catching
(AssertionError, Exception)collapses genuine programming errors (TypeError, AttributeError, etc.) into generic test failures, hiding bugs and contradicting Ruff/BLE001 guidance from the last review. CatchAssertionErroronly for assertion failures, and log & re-raise unexpected exceptions so they surface. Apply the same fix to the non-list branch.tests/README.md (1)
12-15: Remove hardcoded Windows path.Line 14 contains a hardcoded Windows-specific path that won't work for other developers or CI environments.
Apply this diff:
2. Ensure you're in the project root directory: ```bash -cd T:\projects\LLM_LoRa\LLM_LoRa +cd /path/to/LLM_LoRa # Replace with your project path</blockquote></details> <details> <summary>rkllm_files/Dockerfile.rkllm (2)</summary><blockquote> `3-3`: **Missing sources_bionic.list file will cause Docker build to fail.** The `COPY` instruction references `sources_bionic.list` which doesn't exist in the repository, causing Docker build failures. --- `19-21`: **Security: Certificate validation disabled for pip.** Disabling certificate validation with `--trusted-host` exposes the build to man-in-the-middle attacks. Apply this diff to remove insecure options: ```diff -RUN python -m pip install --upgrade pip -i https://pypi.tuna.tsinghua.edu.cn/simple --trusted-host=pypi.tuna.tsinghua.edu.cn +RUN python -m pip install --upgrade pip -i https://pypi.tuna.tsinghua.edu.cn/simple RUN pip3 config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple -RUN pip3 config set install.trusted-host pypi.tuna.tsinghua.edu.cnDockerfile_ollama (1)
6-6: Overly permissive directory permissions pose a security risk.Setting permissions to
777violates the principle of least privilege and could allow unauthorized modifications.Apply this diff:
-RUN mkdir -p /app/models && chmod 777 /app/models +RUN mkdir -p /app/models && chmod 755 /app/modelsdocs/evaluation.rst (1)
96-96: Fix typo: "ROGUE" should be "ROUGE"The metric name is misspelled at line 96 as well.
Apply this diff:
-* **ROGUE Score**: Recall-oriented evaluation for summarization +* **ROUGE Score**: Recall-oriented evaluation for summarizationdocs/training_methods.rst (1)
408-411: Incorrect line continuation in bash example.The bash command uses double backslashes (
\\) instead of single backslashes (\) for line continuation.Apply this diff:
# Evaluate preference-trained model - python main.py evaluate_model \\ - --model_path=models/dpo_model \\ - --evaluation_type=preference \\ + python main.py evaluate_model \ + --model_path=models/dpo_model \ + --evaluation_type=preference \ --metrics=["preference_accuracy", "alignment_score"]docs/api_reference.rst (1)
280-286: Fix duplicate and rewrite documentation to reference actual implementation.The documentation has two critical issues:
Duplicate on line 283:
.. automethod:: training_model.LLMLoRaCLI.convertappears twice consecutively.Non-existent class references: Lines 280-281 and 284-285 reference
main.LLMLoRAFrameworkwith methodstrain_model,evaluate_model,list_models, andclean_checkpoints. This class does not exist in the codebase. Onlytraining_model.LLMLoRaCLIexists in the project.Replace with the actual public methods from
LLMLoRaCLI:.. automethod:: training_model.LLMLoRaCLI.train .. automethod:: training_model.LLMLoRaCLI.optimize .. automethod:: training_model.LLMLoRaCLI.convert -.. automethod:: training_model.LLMLoRaCLI.convert .. automethod:: training_model.LLMLoRaCLI.test .. automethod:: training_model.LLMLoRaCLI.pipelinetraining_model/data_preparation.py (1)
113-118: Drop the redundant importThere’s still a second
import logginginsidedo_transform, even though the module already imports logging at the top. Please remove the inner import.def do_transform() -> None: """One run function to convert the dataset.""" - import logging - logger = logging.getLogger(__name__)docker-compose.yaml (1)
54-55: Attach both services to the same networks
llm_trainingis only onmlflow-networkwhileollamastays on the default bridge, so name resolution forollamastill breaks exactly as noted earlier. Attach both containers to both networks so the training job can reachhttp://ollama:11434.ollama: ... - runtime: nvidia + runtime: nvidia + networks: + - default + - mlflow-network ... llm_training: ... - networks: - - mlflow-network + networks: + - default + - mlflow-networkrun_pipeline.sh (1)
129-134: Fix the JSON payload syntaxThe extra
')'after the jq block is still here, which causesbashto abort with “syntax error near unexpected token ')'”. Please drop that stray parenthesis.JSON_PAYLOAD=$(jq -n \ --arg name "custom-model" \ --arg blob_name "$BLOB_NAME" \ --arg gguf_file "$GGUF_FILE" \ - '{name: $name, files: {($gguf_file): $blob_name}}')') + '{name: $name, files: {($gguf_file): $blob_name}}')testing_model/models.py (1)
281-284: Convert structured output schema before calling OpenAI.
response_formatstill receives the Pydantic class itself, and the async client will raise a serialization error on the first structured call. Please materialize the schema into the{"type": "json_schema", ...}payload that the API expects.if schema is not None: - kwargs["response_format"] = schema + if hasattr(schema, "model_json_schema"): + schema_dict = schema.model_json_schema() + elif hasattr(schema, "schema"): + schema_dict = schema.schema() + else: + raise TypeError("schema must expose model_json_schema() or schema()") + kwargs["response_format"] = { + "type": "json_schema", + "json_schema": { + "name": schema.__name__, + "schema": schema_dict, + "strict": True, + }, + }training_model/dpo_train.py (1)
39-70: Resolve Hydra working-directory handling for DPO data files.Hydra switches
cwdto the run output directory, soPath.cwd() / cfg.paths.data_dirlooks underoutputs/...and both validation and preparation fail to find real datasets—exactly what we discussed earlier. Please derive the base path viahydra.utils.get_original_cwd()(with a safe fallback) in bothvalidate_dpo_configandprepare_dpo_data.- data_dir = Path.cwd() / cfg.paths.data_dir + try: + from hydra.core.hydra_config import HydraConfig + from hydra.utils import get_original_cwd + + if HydraConfig.initialized(): + base_dir = Path(get_original_cwd()) + else: + base_dir = Path.cwd() + except (ImportError, ValueError): + base_dir = Path.cwd() + + data_dir = base_dir / cfg.paths.data_dir @@ - data_dir = Path.cwd() / cfg.paths.data_dir + try: + from hydra.core.hydra_config import HydraConfig + from hydra.utils import get_original_cwd + + if HydraConfig.initialized(): + base_dir = Path(get_original_cwd()) + else: + base_dir = Path.cwd() + except (ImportError, ValueError): + base_dir = Path.cwd() + + data_dir = base_dir / cfg.paths.data_dirtraining_model/config_validation.py (1)
349-357: Read the WandB config fromlogging.wandb.Validation still looks under
cfg.wandb, so every legitimate config withlogging_backend: wandbis rejected—mirroring the earlier feedback. Please inspectcfg.logging.wandbinstead.- if hasattr(cfg, "wandb"): - wandb_cfg = cfg.wandb - if not hasattr(wandb_cfg, "project_name") or not wandb_cfg.project_name: - logger.error("Wandb project name is required") - return False - else: - logger.error("Wandb backend selected but no wandb configuration found") - return False + wandb_cfg = getattr(logging_cfg, "wandb", None) + if not wandb_cfg: + logger.error("Wandb backend selected but no logging.wandb configuration found") + return False + project_name = getattr(wandb_cfg, "project_name", None) + if not project_name: + logger.error("Wandb project name is required") + return Falsetraining_model/grpo_train.py (2)
75-94: Guard against missinggrpo/pathssections before validation.This still dereferences
cfg.grpoandcfg.pathsunconditionally, so configurations that omit either section (which Hydra allows) now fail withAttributeError, exactly as called out in the earlier review. Please reintroduce the existence checks (or reuse the shared validator) before accessing their members and bail out with a clear log message when they’re absent.
758-760: Respect user-specified generation overrides.
generation_configis a plaindict, sogetattr(..., "temperature", 1.0)always falls back to your literals and silently ignores GRPO overrides defined in the config—same regression reported previously. Swap these to.get(...)so custom settings actually flow intoGRPOConfig.- temperature=getattr(generation_config, "temperature", 1.0), - top_p=getattr(generation_config, "top_p", 1.0), - top_k=getattr(generation_config, "top_k", 50), + temperature=generation_config.get("temperature", 1.0), + top_p=generation_config.get("top_p", 1.0), + top_k=generation_config.get("top_k", 50),evaluation/deepeval_integration.py (1)
124-132: Fail fast whenCustomOpenAIModelisn’t imported.
_ensure_models_imported()still leavesCustomOpenAIModelasNonewhen the optional dependency is missing, and_get_custom_api_model()immediately calls it, triggeringTypeError: 'NoneType' object is not callable—the same regression raised in the prior review. Please guard after the lazy import (and optionally invoke_load_environment_if_needed()so.envloading matches the other paths) before instantiating._ensure_models_imported() + _load_environment_if_needed() + if CustomOpenAIModel is None: + raise RuntimeError( + "CustomOpenAIModel not available — install Deepeval custom API dependencies." + )
🧹 Nitpick comments (10)
training_model/exceptions.py (1)
7-8: Consider adding an__all__export list.Adding an explicit
__all__list would document the public API and improve IDE support.Apply this diff to add the export list:
""" +__all__ = [ + "LLMLoRaError", + "ConfigurationError", + "ModelLoadingError", + "TrainingError", + "ConversionError", + "DataProcessingError", + "ExperimentTrackingError", + "ResourceManagementError", +] + + class LLMLoRaError(Exception):tools/dataset/find_threat_question_problems.py (1)
60-60: Move import to top of file.Importing
defaultdictmid-file violates PEP 8 conventions. All imports should be placed at the module's top.Apply this diff:
import json +from collections import defaultdict from pathlib import Path data_path = Path("T:/projects/LLM_LoRa/LLM_LoRa/data/dataset_ru_extended.json")Then remove line 60:
# Group by type -from collections import defaultdict - by_type = defaultdict(list)tools/dataset/find_question_action_problems.py (2)
39-39: Move import to top of file.Importing
defaultdictmid-file violates PEP 8 conventions.Apply this diff:
import json +from collections import defaultdict from pathlib import PathThen remove line 39:
# Group by action type -from collections import defaultdict - by_action = defaultdict(list)
47-52: Remove unused loop variable.The
reasonvariable is unpacked in the loop but never used. Consider using_for unused variables or remove if unnecessary.Apply this diff:
for action, items in sorted(by_action.items()): print(f"\n{action}: {len(items)} examples") - for i, (key, msg, reason) in enumerate(items[:5], 1): + for i, (key, msg, _) in enumerate(items[:5], 1): msg_short = msg[:70] + "..." if len(msg) > 70 else msg print(f" {i}. {key}") print(f' "{msg_short}"')evaluation/game_evaluation.py (1)
42-44: Simplify by inlining the exception raise.The
_raise_action_mismatch()helper adds unnecessary indirection for a single raise statement. Consider inlining it directly at line 30.Apply this diff:
predicted_action = model_answer_dict["Content"]["Action"] correct_action = correct_answer_dict["Content"]["Action"] if predicted_action != correct_action: - _raise_action_mismatch() + raise AssertionError("Action doesn't match") except JSONDecodeError as err: error_msg = "Model answer or correct answer is not in JSON format" raise AssertionError(error_msg) from errThen remove the helper function:
- - -def _raise_action_mismatch() -> None: - """Helper to raise a standardized AssertionError for action mismatch.""" - raise AssertionError("Action doesn't match")tools/dataset/find_future_tense_problems.py (1)
52-52: Move import to top of file.Importing
defaultdictmid-file violates PEP 8 conventions.Apply this diff:
import json +from collections import defaultdict from pathlib import PathThen remove line 52:
# Group by action -from collections import defaultdict - by_action = defaultdict(list)tools/dataset/analyze_dataset.py (1)
59-68: Remove unused loop variable.The
keyvariable is declared but never used in the loop body.Apply this diff:
# Incomplete examples incomplete = 0 -for key, example in examples.items(): +for example in examples.values(): prompt_data = example.get("prompt", {}) answer_data = example.get("answer", {})training_model/__init__.py (1)
71-77: Improve numeric string detection.The current logic for detecting numeric strings on line 76 may have edge cases. For example, multiple minus signs would pass the digit check after replacement.
Use explicit type conversion with exception handling:
for key, raw_value in filtered_overrides.items(): value = raw_value - # Convert string values to appropriate types - if isinstance(value, str) and value.lower() in ["true", "false"]: - value = value.lower() == "true" - elif isinstance(value, str) and value.replace(".", "").replace("-", "").isdigit(): - value = float(value) if "." in value else int(value) + # Convert string values to appropriate types + if isinstance(value, str): + if value.lower() in ["true", "false"]: + value = value.lower() == "true" + else: + # Try numeric conversion + try: + value = int(value) + except ValueError: + try: + value = float(value) + except ValueError: + pass # Keep as stringtests/integration/test_training_functions.py (1)
58-62: Consider honoring tokenize parameter for completeness.The mock
apply_chat_templateaccepts but ignores thetokenizeparameter. While current tests only usetokenize=False, honoring this parameter would make the mock more realistic.def apply_chat_template(messages: list, tokenize: bool = False) -> str | list: parts = [f"{m['role'].upper()}: {m['content']}" for m in messages] text = "\n".join(parts) if tokenize: return tok.encode(text) return texttesting_model/__main__.py (1)
65-145: Log exceptions and mark intentionally unused argumentsPlease switch the
logger.errorcalls inside the wrappers tologger.exceptionso we keep stack traces, and prefix the intentionally unusedcorrect_answerparameters with an underscore (while updating the call site in the first wrapper). This satisfies Ruff’s TRY400/ARG001 findings and makes the intent explicit.async def wrapper_unintended_answer_mention( - model_answer: str, correct_answer: str, **kwargs: dict + model_answer: str, _correct_answer: str, **kwargs: dict ) -> tuple[bool, float, str]: ... - return await test_unintended_answer_mention_async( - cfg, user_input, model_answer, correct_answer - ) + return await test_unintended_answer_mention_async( + cfg, user_input, model_answer, _correct_answer + ) except Exception as e: - logger.error( + logger.exception( f"Failed to initialize evaluation model for unintended_answer_mention: {e}" ) logger.error( "Check your DeepEval config and API credentials (MISTRAL_API, CUSTOM_API_KEY)" ) raise async def wrapper_russian_language_quality( - model_answer: str, correct_answer: str, **kwargs: dict + model_answer: str, _correct_answer: str, **_kwargs: dict ) -> tuple[bool, float, str]: ... except Exception as e: - logger.error( + logger.exception( f"Failed to initialize evaluation model for russian_language_quality: {e}" ) logger.error( "Check your DeepEval config and API credentials (MISTRAL_API, CUSTOM_API_KEY)" ) raise async def wrapper_game_context_appropriateness( - model_answer: str, correct_answer: str, **kwargs: dict + model_answer: str, _correct_answer: str, **kwargs: dict ) -> tuple[bool, float, str]: ... except Exception as e: - logger.error( + logger.exception( f"Failed to initialize evaluation model for game_context_appropriateness: {e}" ) logger.error( "Check your DeepEval config and API credentials (MISTRAL_API, CUSTOM_API_KEY)" ) raise
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
data/translate.csvis excluded by!**/*.csvpoetry.lockis excluded by!**/*.lock
📒 Files selected for processing (73)
.dvc/.gitignore(1 hunks).dvc/config(1 hunks).dvcignore(1 hunks).flake8(0 hunks).gitignore(1 hunks).pre-commit-config.yaml(1 hunks)Dockerfile(3 hunks)Dockerfile_cuda(1 hunks)Dockerfile_ollama(1 hunks)README.md(1 hunks)conf/config.yaml(1 hunks)data.dvc(1 hunks)data/daedalus_eng.json(0 hunks)data/test_eng.json(0 hunks)data/test_ru.json(0 hunks)data/values.txt(0 hunks)dev.py(1 hunks)docker-compose.yaml(1 hunks)docs/CONTRIBUTING.md(1 hunks)docs/README_RU.md(1 hunks)docs/api_reference.rst(1 hunks)docs/conf.py(2 hunks)docs/docker_deployment.rst(1 hunks)docs/evaluation.rst(1 hunks)docs/fire_cli.rst(1 hunks)docs/hydra_config.rst(7 hunks)docs/index.rst(1 hunks)docs/requirements.txt(1 hunks)docs/rkllm_conversion.rst(1 hunks)docs/testing_model.rst(0 hunks)docs/training_methods.rst(1 hunks)docs/training_model.rst(1 hunks)evaluation/__init__.py(1 hunks)evaluation/deepeval_integration.py(1 hunks)evaluation/game_evaluation.py(3 hunks)evaluation/model_evaluation.py(1 hunks)legacy/llama31_train.py(0 hunks)main.py(1 hunks)model.dvc(1 hunks)mypy.ini(0 hunks)pyproject.toml(2 hunks)requirements.txt(1 hunks)rkllm_files/Dockerfile.rkllm(1 hunks)rkllm_files/entrypoint.sh(1 hunks)rkllm_files/rkllm_converting.py(1 hunks)run_pipeline.sh(3 hunks)testing_model/__init__.py(1 hunks)testing_model/__main__.py(3 hunks)testing_model/deepeval_func.py(0 hunks)testing_model/models.py(1 hunks)testing_model/test.py(0 hunks)tests/README.md(1 hunks)tests/__init__.py(1 hunks)tests/integration/test_training_functions.py(1 hunks)tests/test_requirements.txt(1 hunks)tests/unit/test_dpo_grpo.py(1 hunks)tools/dataset/analyze_dataset.py(1 hunks)tools/dataset/analyze_game_coverage.py(1 hunks)tools/dataset/find_future_tense_problems.py(1 hunks)tools/dataset/find_history_ending_problems.py(1 hunks)tools/dataset/find_question_action_problems.py(1 hunks)tools/dataset/find_threat_question_problems.py(1 hunks)tools/dataset/validate_extended_dataset.py(1 hunks)training_model/__init__.py(1 hunks)training_model/__main__.py(3 hunks)training_model/config_validation.py(1 hunks)training_model/data_preparation.py(1 hunks)training_model/dpo_train.py(1 hunks)training_model/exceptions.py(1 hunks)training_model/grpo_train.py(5 hunks)training_model/logging_config.py(2 hunks)training_model/logging_utils.py(1 hunks)training_model/memory_utils.py(1 hunks)
💤 Files with no reviewable changes (10)
- data/daedalus_eng.json
- data/values.txt
- docs/testing_model.rst
- mypy.ini
- data/test_ru.json
- data/test_eng.json
- testing_model/deepeval_func.py
- .flake8
- testing_model/test.py
- legacy/llama31_train.py
🧰 Additional context used
🧬 Code graph analysis (17)
testing_model/__main__.py (5)
evaluation/game_evaluation.py (1)
test_actions(7-39)evaluation/model_evaluation.py (2)
dataset_to_json_for_test(86-119)test_llm(904-969)training_model/logging_config.py (1)
configure_logging(73-119)evaluation/deepeval_integration.py (7)
test_game_context_appropriateness(304-355)test_game_context_appropriateness_async(436-484)test_russian_language_quality(279-301)test_russian_language_quality_async(400-433)test_unintended_answer_mention(235-276)test_unintended_answer_mention_async(358-397)_get_evaluation_model(134-162)training_model/__init__.py (4)
wrapper_unintended_answer_mention(321-327)wrapper_russian_language_quality(329-332)wrapper_game_context_appropriateness(334-341)test(253-370)
run_pipeline.sh (1)
training_model/__init__.py (1)
pipeline(372-424)
training_model/__main__.py (4)
evaluation/model_evaluation.py (2)
dataset_to_json_for_test(86-119)test_llm(904-969)training_model/logging_config.py (1)
configure_logging(73-119)training_model/one_file_train.py (1)
main_train(1934-1976)training_model/optuna.py (1)
optuna_optimize(111-190)
training_model/__init__.py (7)
evaluation/model_evaluation.py (2)
dataset_to_json_for_test(86-119)test_llm(904-969)training_model/config_validation.py (2)
get_validation_summary(434-468)validate_complete_config(393-431)training_model/logging_config.py (1)
configure_logging(73-119)training_model/one_file_train.py (3)
convert_to_gguf(1266-1331)convert_to_rkllm(1334-1511)train(934-1171)training_model/optuna.py (1)
optuna_optimize(111-190)evaluation/game_evaluation.py (1)
test_actions(7-39)evaluation/deepeval_integration.py (3)
test_game_context_appropriateness(304-355)test_russian_language_quality(279-301)test_unintended_answer_mention(235-276)
testing_model/__init__.py (3)
evaluation/deepeval_integration.py (2)
test_from_dataset(558-619)test_mention_number_of_values(194-232)evaluation/model_evaluation.py (2)
test_llm(904-969)test_via_llamacpp(1054-1144)testing_model/models.py (3)
CustomLocalModel(12-85)CustomMistralModel(88-198)CustomOpenAIModel(201-294)
tests/integration/test_training_functions.py (1)
training_model/one_file_train.py (8)
change_dir(169-186)convert_to_gguf(1266-1331)data_preparation(447-559)generate_and_tokenize_prompt(244-280)generate_prompt(189-216)model_merge_for_converting(562-634)quantize_model(1680-1735)tokenize(219-241)
training_model/dpo_train.py (3)
training_model/logging_utils.py (3)
get_report_to_backend(11-33)log_dataset_samples(263-350)mlflow_phase_run(37-78)training_model/memory_utils.py (2)
comprehensive_memory_cleanup(55-107)log_memory_usage(35-52)training_model/optimizer_factory.py (2)
create_optimizer(154-173)get_optimizer_config_updates(176-197)
training_model/config_validation.py (2)
training_model/dpo_train.py (1)
validate_dpo_config(19-52)training_model/grpo_train.py (1)
validate_grpo_config(66-96)
training_model/logging_utils.py (1)
training_model/logging_config.py (1)
log_dict(134-158)
training_model/logging_config.py (1)
rkllm_files/entrypoint.sh (1)
log(8-10)
tests/unit/test_dpo_grpo.py (2)
training_model/dpo_train.py (3)
dpo_train(141-305)prepare_dpo_data(55-138)validate_dpo_config(19-52)training_model/grpo_train.py (8)
grpo_train(556-863)debug_reward_function(99-146)prepare_grpo_data(444-553)reward_function(149-233)validate_grpo_config(66-96)reward_function_json_validation(236-289)reward_function_action_extraction(292-400)reward_function_combined(403-441)
training_model/data_preparation.py (1)
evaluation/model_evaluation.py (1)
get_user_prompt(32-61)
dev.py (2)
evaluation/model_evaluation.py (1)
run_tests(357-476)training_model/logging_config.py (1)
format(59-70)
training_model/grpo_train.py (4)
training_model/logging_utils.py (3)
get_report_to_backend(11-33)log_dataset_samples(263-350)mlflow_phase_run(37-78)training_model/memory_utils.py (2)
comprehensive_memory_cleanup(55-107)log_memory_usage(35-52)training_model/optimizer_factory.py (2)
create_optimizer(154-173)get_optimizer_config_updates(176-197)training_model/utils.py (1)
get_generation_config(17-92)
main.py (1)
training_model/__init__.py (1)
LLMLoRaCLI(32-546)
evaluation/deepeval_integration.py (2)
testing_model/models.py (3)
CustomLocalModel(12-85)CustomMistralModel(88-198)CustomOpenAIModel(201-294)training_model/utils.py (1)
_load_environment_if_needed(95-103)
evaluation/model_evaluation.py (3)
evaluation/game_evaluation.py (1)
test_actions(7-39)training_model/data_preparation.py (1)
get_user_prompt(12-23)training_model/__init__.py (1)
test(253-370)
🪛 Checkov (3.2.334)
rkllm_files/Dockerfile.rkllm
[high] 19-19: Ensure that certificate validation isn't disabled with the pip '--trusted-host' option
(CKV2_DOCKER_4)
🪛 GitHub Actions: Lint with pre-commit
.pre-commit-config.yaml
[error] 1-1: Command failed with exit code 1. pre-commit hook failures: end-of-file-fixer, trailing-whitespace, mixed-line-ending, ruff, ruff-format. Some files were modified by hooks; review changes.
🪛 LanguageTool
docs/README_RU.md
[uncategorized] ~173-~173: Перед знаками препинания не должно быть пробела: «конфигурация:»
Context: ...е файлы: - conf/config.yaml: Основная конфигурация - conf/model/: Настройки моделей - conf/training/: Г...
(Punct_and_spaces)
[uncategorized] ~174-~174: Перед знаками препинания не должно быть пробела: «моделей:»
Context: ...конфигурация - conf/model/: Настройки моделей - conf/training/: Гиперпараметры обучения ### Пример кон...
(Punct_and_spaces)
docs/CONTRIBUTING.md
[grammar] ~262-~262: Use a hyphen to join words.
Context: ...tion Style - Docstrings: Use Google style docstrings - Comments: Explain...
(QB_NEW_EN_HYPHEN)
[uncategorized] ~443-~443: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...king changes - MINOR: New features, backwards compatible - PATCH: Bug fixes, backwards compa...
(EN_COMPOUND_ADJECTIVE_INTERNAL)
🪛 Ruff (0.14.2)
testing_model/__main__.py
87-89: Use logging.exception instead of logging.error
Replace with exception
(TRY400)
90-92: Use logging.exception instead of logging.error
Replace with exception
(TRY400)
96-96: Unused function argument: correct_answer
(ARG001)
96-96: Unused function argument: kwargs
(ARG001)
108-110: Use logging.exception instead of logging.error
Replace with exception
(TRY400)
111-113: Use logging.exception instead of logging.error
Replace with exception
(TRY400)
117-117: Unused function argument: correct_answer
(ARG001)
139-141: Use logging.exception instead of logging.error
Replace with exception
(TRY400)
142-144: Use logging.exception instead of logging.error
Replace with exception
(TRY400)
tools/dataset/analyze_dataset.py
59-59: Loop control variable key not used within loop body
Rename unused key to _key
(B007)
training_model/__init__.py
5-5: Unused noqa directive (non-enabled: F401)
Remove unused noqa directive
(RUF100)
116-116: Avoid specifying long messages outside the exception class
(TRY003)
160-160: Avoid specifying long messages outside the exception class
(TRY003)
330-330: Unused function argument: correct_answer
(ARG001)
330-330: Unused function argument: kwargs
(ARG001)
335-335: Unused function argument: correct_answer
(ARG001)
405-405: Avoid specifying long messages outside the exception class
(TRY003)
tests/integration/test_training_functions.py
58-58: Unused function argument: tokenize
(ARG001)
training_model/dpo_train.py
165-165: Avoid specifying long messages outside the exception class
(TRY003)
193-193: Avoid specifying long messages outside the exception class
(TRY003)
evaluation/game_evaluation.py
7-7: Unused function argument: kwargs
(ARG001)
44-44: Avoid specifying long messages outside the exception class
(TRY003)
training_model/config_validation.py
169-169: Do not catch blind exception: Exception
(BLE001)
181-181: Redundant exception object included in logging.exception call
(TRY401)
424-424: Redundant exception object included in logging.exception call
(TRY401)
464-464: Do not catch blind exception: Exception
(BLE001)
tools/dataset/find_question_action_problems.py
47-47: Loop control variable reason not used within loop body
(B007)
training_model/logging_utils.py
70-71: Remove exception handler; error is immediately re-raised
(TRY203)
103-103: Do not catch blind exception: Exception
(BLE001)
144-144: Do not catch blind exception: Exception
(BLE001)
188-188: Do not catch blind exception: Exception
(BLE001)
222-222: Do not catch blind exception: Exception
(BLE001)
256-256: Consider moving this statement to an else block
(TRY300)
259-259: Redundant exception object included in logging.exception call
(TRY401)
349-349: Do not catch blind exception: Exception
(BLE001)
training_model/logging_config.py
93-93: Avoid specifying long messages outside the exception class
(TRY003)
97-97: Avoid specifying long messages outside the exception class
(TRY003)
tests/unit/test_dpo_grpo.py
632-632: Unused method argument: mock_dpo_config
(ARG002)
700-700: Unused method argument: mock_grpo_config
(ARG002)
dev.py
1-1: Shebang is present but file is not executable
(EXE001)
40-40: Do not catch blind exception: Exception
(BLE001)
97-97: subprocess call: check for execution of untrusted input
(S603)
339-339: Abstract raise to an inner function
(TRY301)
339-339: Avoid specifying long messages outside the exception class
(TRY003)
340-340: subprocess call: check for execution of untrusted input
(S603)
351-351: Abstract raise to an inner function
(TRY301)
351-351: Avoid specifying long messages outside the exception class
(TRY003)
353-353: subprocess call: check for execution of untrusted input
(S603)
377-377: Do not catch blind exception: Exception
(BLE001)
390-390: Do not catch blind exception: Exception
(BLE001)
396-396: Do not catch blind exception: Exception
(BLE001)
training_model/memory_utils.py
139-139: Do not catch blind exception: Exception
(BLE001)
training_model/grpo_train.py
226-229: Use logging.exception instead of logging.error
Replace with exception
(TRY400)
236-236: Unused function argument: kwargs
(ARG001)
285-285: Do not catch blind exception: Exception
(BLE001)
286-286: Use logging.exception instead of logging.error
Replace with exception
(TRY400)
396-396: Do not catch blind exception: Exception
(BLE001)
397-397: Use logging.exception instead of logging.error
Replace with exception
(TRY400)
579-579: Avoid specifying long messages outside the exception class
(TRY003)
672-672: Avoid specifying long messages outside the exception class
(TRY003)
evaluation/deepeval_integration.py
30-30: Do not catch blind exception: Exception
(BLE001)
44-44: Do not catch blind exception: Exception
(BLE001)
71-71: Avoid specifying long messages outside the exception class
(TRY003)
95-95: Unused function argument: cfg
(ARG001)
99-99: Avoid specifying long messages outside the exception class
(TRY003)
162-162: Avoid specifying long messages outside the exception class
(TRY003)
184-184: subprocess call: check for execution of untrusted input
(S603)
219-219: String contains ambiguous а (CYRILLIC SMALL LETTER A). Did you mean a (LATIN SMALL LETTER A)?
(RUF001)
236-236: Unused function argument: expected_answer
(ARG001)
263-263: String contains ambiguous а (CYRILLIC SMALL LETTER A). Did you mean a (LATIN SMALL LETTER A)?
(RUF001)
359-359: Unused function argument: expected_answer
(ARG001)
383-383: String contains ambiguous а (CYRILLIC SMALL LETTER A). Did you mean a (LATIN SMALL LETTER A)?
(RUF001)
541-541: Do not catch blind exception: Exception
(BLE001)
544-544: Use logging.exception instead of logging.error
Replace with exception
(TRY400)
560-560: Unused function argument: test_dataset
(ARG001)
testing_model/models.py
25-25: Unused method argument: args
(ARG002)
26-26: Unused method argument: kwargs
(ARG002)
105-105: Unused method argument: args
(ARG002)
106-106: Unused method argument: kwargs
(ARG002)
219-219: Unused method argument: args
(ARG002)
220-220: Unused method argument: kwargs
(ARG002)
evaluation/model_evaluation.py
49-49: String contains ambiguous с (CYRILLIC SMALL LETTER ES). Did you mean c (LATIN SMALL LETTER C)?
(RUF001)
49-49: String contains ambiguous о (CYRILLIC SMALL LETTER O). Did you mean o (LATIN SMALL LETTER O)?
(RUF001)
49-49: String contains ambiguous р (CYRILLIC SMALL LETTER ER). Did you mean p (LATIN SMALL LETTER P)?
(RUF001)
54-54: String contains ambiguous Т (CYRILLIC CAPITAL LETTER TE). Did you mean T (LATIN CAPITAL LETTER T)?
(RUF001)
58-58: String contains ambiguous О (CYRILLIC CAPITAL LETTER O). Did you mean O (LATIN CAPITAL LETTER O)?
(RUF001)
58-58: String contains ambiguous е (CYRILLIC SMALL LETTER IE). Did you mean e (LATIN SMALL LETTER E)?
(RUF001)
275-275: Do not catch blind exception: Exception
(BLE001)
558-558: Redundant exception object included in logging.exception call
(TRY401)
612-612: Unused noqa directive (non-enabled: SIM113)
Remove unused noqa directive
(RUF100)
618-618: Redundant exception object included in logging.exception call
(TRY401)
699-699: Abstract raise to an inner function
(TRY301)
711-711: Do not catch blind exception: Exception
(BLE001)
734-734: Unpacked variable score is never used
Prefix it with an underscore or any other dummy variable pattern
(RUF059)
738-738: Abstract raise to an inner function
(TRY301)
751-751: Do not catch blind exception: Exception
(BLE001)
773-773: Redundant exception object included in logging.exception call
(TRY401)
828-828: Unused noqa directive (non-enabled: SIM113)
Remove unused noqa directive
(RUF100)
963-963: Use logging.exception instead of logging.error
Replace with exception
(TRY400)
964-964: Redundant exception object included in logging.exception call
(TRY401)
985-985: Docstring contains ambiguous с (CYRILLIC SMALL LETTER ES). Did you mean c (LATIN SMALL LETTER C)?
(RUF002)
992-992: Docstring contains ambiguous с (CYRILLIC SMALL LETTER ES). Did you mean c (LATIN SMALL LETTER C)?
(RUF002)
1066-1066: Docstring contains ambiguous с (CYRILLIC SMALL LETTER ES). Did you mean c (LATIN SMALL LETTER C)?
(RUF002)
1071-1071: Docstring contains ambiguous с (CYRILLIC SMALL LETTER ES). Did you mean c (LATIN SMALL LETTER C)?
(RUF002)
1114-1114: String contains ambiguous – (EN DASH). Did you mean - (HYPHEN-MINUS)?
(RUF001)
1115-1115: String contains ambiguous У (CYRILLIC CAPITAL LETTER U). Did you mean Y (LATIN CAPITAL LETTER Y)?
(RUF001)
1116-1116: String contains ambiguous с (CYRILLIC SMALL LETTER ES). Did you mean c (LATIN SMALL LETTER C)?
(RUF001)
1117-1117: String contains ambiguous с (CYRILLIC SMALL LETTER ES). Did you mean c (LATIN SMALL LETTER C)?
(RUF001)
rkllm_files/rkllm_converting.py
1-1: Shebang is present but file is not executable
(EXE001)
114-114: Avoid specifying long messages outside the exception class
(TRY003)
190-190: Consider moving this statement to an else block
(TRY300)
193-193: Redundant exception object included in logging.exception call
(TRY401)
🪛 Shellcheck (0.11.0)
run_pipeline.sh
[warning] 76-76: Quote this to prevent word splitting.
(SC2046)
[warning] 77-77: Quote this to prevent word splitting.
(SC2046)
[error] 124-124: Parsing stopped here. Is this keyword correctly matched up?
(SC1089)
🔇 Additional comments (24)
training_model/exceptions.py (1)
1-95: Well-structured exception hierarchy!The exception classes are well-organized with a clear inheritance hierarchy and comprehensive docstrings. Each exception type has specific, documented use cases that will aid debugging and error handling throughout the framework.
model.dvc (1)
1-6: LGTM! Valid DVC model tracking configuration.The DVC metadata correctly tracks the model directory with appropriate hash, size, and file count information.
tests/__init__.py (1)
1-2: LGTM! Standard package marker.This correctly makes the tests directory a Python package.
.dvc/.gitignore (1)
1-3: LGTM! Standard DVC ignore patterns.These patterns correctly exclude local DVC configuration and cache directories from version control.
.dvcignore (1)
1-3: LGTM! Valid initial .dvcignore setup.The file is correctly structured with documentation reference. Patterns can be added later as needed.
data.dvc (1)
1-6: LGTM! Valid DVC data tracking configuration.The DVC metadata correctly tracks the data directory with appropriate hash, size, and file count information.
.dvc/config (2)
1-3: LGTM! Appropriate DVC cache settings.The hardlink cache type efficiently manages disk space, and protected mode prevents accidental cache corruption.
4-7: Document DVC remote configuration and setup requirements.The DVC remote configuration uses an SMB/UNC path (
//diskstation_105/dvc_storage) but lacks documentation. While no active DVC commands are found in the codebase, this should still be documented to prevent issues if the remote is used in the future or if team members need to set up the project.Recommendations:
- Add DVC setup instructions to README or a dedicated setup guide, including SMB/CIFS requirements for different OS
- Clarify whether the NAS is required for all team members or only specific workflows
- Document fallback options for environments without NAS access (e.g., local storage or cloud alternatives)
.gitignore (1)
195-203: No issues found. The ignored patterns are correctly designated for personal/temporary files.Verification confirms these files don't exist in the repository, were never committed, and are appropriately excluded from version control. These are personal/temporary files (configuration, notes, and local instructions) that the developer correctly prevents from being tracked.
README.md (1)
1-596: Excellent documentation improvements!The README has been significantly enhanced with comprehensive sections covering installation, configuration, usage examples, troubleshooting, and more. The structure is clear, well-organized, and provides valuable guidance for users at all levels.
tests/test_requirements.txt (1)
1-22: LGTM! Well-structured test requirements file.The test requirements file is well-organized with clear comments, appropriate minimum version specifications, and good separation of concerns between core testing frameworks and additional utilities.
docs/index.rst (1)
1-109: LGTM! Comprehensive documentation structure.The documentation homepage is well-organized with clear sections covering features, supported models, quick start, architecture, and navigation to detailed documentation. The structure effectively guides users through the framework's capabilities.
Dockerfile_cuda (1)
26-30: LGTM! Proper CUDA-enabled llama.cpp build.The llama.cpp build configuration correctly handles CUDA support with appropriate workarounds for the libcuda.so stub, uses Release mode, and leverages parallel compilation for efficiency.
rkllm_files/entrypoint.sh (1)
1-51: Well-structured entrypoint script.The script properly handles container lifecycle with appropriate error management (set -e, error function), includes logging with timestamps, and correctly routes the convert command to the Python backend. The idle mode using
tail -f /dev/nullkeeps the container running when no command is provided.One optional enhancement: consider adding a trap to clean up resources on exit:
cleanup() { log "Container shutting down..." # Add cleanup logic here if needed } trap cleanup EXIT INT TERMThis ensures graceful shutdown handling, though the current implementation is functional for the intended use case.
docs/docker_deployment.rst (1)
1-50: Comprehensive Docker deployment documentation.The documentation provides clear guidance on containerized deployment with well-organized sections covering base images, Docker Compose setup, Ollama integration, and production configurations. Examples are concrete and follow Docker best practices (non-root users, health checks, security considerations).
Verify that all referenced service names and paths match your actual repository structure:
- Service names in docker-compose examples (e.g.,
llmlora:train,ollama,training,inference)- Dockerfile paths (e.g.,
Dockerfile.train,Dockerfile.ollama)- Volume mount paths and configurations
docs/rkllm_conversion.rst (1)
1-50: Well-structured RKLLM conversion documentation.The documentation comprehensively covers the conversion workflow with clear platform support tables, performance characteristics, and step-by-step processes. The progression from prerequisites through advanced deployment scenarios is logical and actionable.
Verify that the RKLLM toolkit API and imports referenced in the code examples (lines 222-254) match your
rkllm-toolkitversion:
- Import statement:
from rkllm import RKLLM- Methods:
load_onnx(),build(),export_rkllm()- Configuration parameters and return codes
These details are critical for users following the step-by-step conversion guide.
docs/training_model.rst (1)
1-110: Clear training module documentation overview.The documentation appropriately structures the training_model package documentation with clear descriptions of supported training methods (SFT, DPO, GRPO) and a logical workflow progression. The submodule organization with automodule directives follows Sphinx best practices.
Verify that all referenced submodules and their public members are properly exported:
- Ensure each module listed (one_file_train, dpo_train, grpo_train, optuna, auth_utils, data_preparation, logging_config) exposes the documented
__all__list or uses module-level documentation- Confirm that automodule directives will generate correctly by checking module imports and public API visibility
docs/README_RU.md (1)
1-100: Comprehensive Russian README documentation.The Russian README provides thorough project documentation with clear sections covering setup, features, configuration, usage, and troubleshooting. The content is well-organized and mirrors the structure of the English version while maintaining idiomatic Russian.
Note on static analysis hints: LanguageTool flagged punctuation spacing at lines 173-174 regarding colons in the "Ключевые конфигурационные файлы:" section. These appear to be false positives, as the spacing is correct for the bullet-list context used in your configuration example. The formatting is appropriate.
docs/CONTRIBUTING.md (1)
260-287: Comprehensive contributing guidelines.The document provides clear, actionable guidance for contributors with well-organized sections covering development setup, testing, documentation standards, and the PR process. Concrete commands and templates make it easy for contributors to follow the workflow.
Two minor grammar corrections suggested by static analysis (should use hyphens for compound adjectives):
- Line 262: Change
"Use Google style docstrings"to"Use Google-style docstrings"- Line 443: Change
"backwards compatible"to"backwards-compatible"These are minor nitpicks but align with English grammar best practices for compound adjectives.
requirements.txt (1)
1-50: Updated dependency manifest with CUDA 12.4 support.The requirements have been refreshed to use the cu124 PyTorch wheel index and updated package versions across the ML/AI stack. The dependency set comprehensively covers training, evaluation, serving, and monitoring needs with appropriate version constraints.
Verify CUDA 12.4 (cu124) compatibility for key packages, especially:
torch==2.6.0+cu124andtorchvision==0.21.0+cu124: Confirm these versions are stable and compatible with your target GPUsbitsandbytes==0.46.1: Verify it supports CUDA 12.4 (bitsandbytes has historically had CUDA compatibility issues)transformers==4.55.0: Ensure this recent version is stable and compatible with PyTorch 2.6.0You can verify these by:
pip install -r requirements.txt --dry-run # or test in a clean environmenttraining_model/__main__.py (2)
37-48: Verify path resolution robustness.The data directory resolution uses
parents[1]which assumestraining_model/__main__.pyis exactly two levels deep from the project root. This works currently but is fragile if the file moves or the project structure changes.Consider verifying this assumption holds across deployment scenarios or making it more robust by searching upward for a marker file (e.g.,
pyproject.toml,.git):def find_project_root(start_path: Path) -> Path: """Find project root by looking for marker files.""" current = start_path.resolve() for parent in [current] + list(current.parents): if (parent / "pyproject.toml").exists() or (parent / ".git").exists(): return parent return current # fallback to current directory
49-52: LGTM!The conditional Optuna optimization flow is well-structured, cleanly branching between optimization and standard training modes.
testing_model/__init__.py (1)
1-16: LGTM!The public API reorganization cleanly exposes the new model wrappers and test utilities. Imports are well-structured and the
__all__list properly documents the public surface.training_model/grpo_train.py (1)
47-54:isinstancewith a union literal will crash immediately.
isinstance(param, Params4bit | Int8Params)raisesTypeError: isinstance() argument 2 cannot be a unionon Python 3.10+, so the conversion loop blows up before touching any parameters. Please switch to a tuple so execution reaches the intended skip path.- if isinstance(param, Params4bit | Int8Params): + if isinstance(param, (Params4bit, Int8Params)): continueLikely an incorrect or invalid review comment.
| # Install Docker CLI so the training container can control Docker to run the RKLLM converter | ||
| RUN mkdir -p /etc/apt/keyrings && \ | ||
| curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg && \ | ||
| echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" \ | ||
| > /etc/apt/sources.list.d/docker.list && \ | ||
| apt-get update && apt-get install -y docker-ce-cli && rm -rf /var/lib/apt/lists/* |
There was a problem hiding this comment.
🧩 Analysis chain
Docker-in-Docker pattern requires security consideration.
Installing Docker CLI allows the training container to control the host Docker daemon (typically via mounted /var/run/docker.sock). This grants the container significant privileges and should be carefully controlled.
Ensure that:
- The socket mount is only enabled when RKLLM conversion is needed
- Access is restricted in production environments
- Alternative approaches (e.g., separate conversion service) have been considered
If docker-compose.yaml mounts the socket, verify it's conditionally enabled:
🏁 Script executed:
#!/bin/bash
# Check if docker-compose.yaml mounts docker.sock
rg -n "docker.sock" docker-compose.yaml docker-compose.ymlLength of output: 131
🏁 Script executed:
#!/bin/bash
# Examine docker-compose.yaml around line 47 to understand context
sed -n '35,55p' docker-compose.yamlLength of output: 456
🏁 Script executed:
#!/bin/bash
# View entire docker-compose.yaml to understand service structure
cat docker-compose.yamlLength of output: 1337
🏁 Script executed:
#!/bin/bash
# Check if Dockerfile contains any docker commands/usage
rg -n "docker" Dockerfile | head -20Length of output: 420
Docker socket mount is unconditional and should be restricted to converter operations only.
The socket mount on llm_training service (docker-compose.yaml line 47) is enabled unconditionally with full read-write access, despite the Dockerfile comment stating it's only needed for RKLLM converter. The converter runs as a separate gated service (profiles: ["converter"]) but lacks the socket mount, creating an inconsistent and over-privileged setup.
Fix required:
- Either mount the socket only on
rkllm_converterservice (with conditional profile), OR - Conditionally enable the mount on
llm_trainingvia profiles/environment variable when conversion is needed
This ensures the host Docker daemon is only accessible when necessary.
| COPY pyproject.toml poetry.lock ./ | ||
| RUN pip install poetry && \ | ||
| pip install pyyaml && \ | ||
| poetry lock --no-interaction --no-ansi || true && \ | ||
| poetry install --no-root --only main --no-interaction --no-ansi |
There was a problem hiding this comment.
Poetry lock failure silently ignored.
Line 51 uses || true to ignore poetry lock failures. This may lead to inconsistent dependencies across container builds, as lock file generation errors won't be detected.
Consider handling lock failures more explicitly:
# Try to use existing lock, but allow regeneration if corrupted
RUN pip install poetry && \
pip install pyyaml && \
(poetry lock --check || poetry lock --no-interaction --no-ansi) && \
poetry install --no-root --only main --no-interaction --no-ansi🤖 Prompt for AI Agents
Dockerfile around lines 48 to 52: the Dockerfile currently silences poetry lock
failures with "|| true", which can hide dependency lock issues and produce
inconsistent builds; replace the silent ignore with an explicit check/regenerate
flow so the build either validates the existing lock or regenerates it, e.g.,
run poetry lock --check and if that fails run poetry lock --no-interaction
--no-ansi, then run poetry install, and do not swallow errors so the build fails
on unexpected lock errors.
| # Create symlink for quantize executable to match Windows naming convention | ||
| RUN ln -s /llama.cpp/build/bin/llama-quantize /app/llama.cpp/llama-quantize.exe |
There was a problem hiding this comment.
🧩 Analysis chain
Verify .exe extension requirement on Linux.
The symlink creates llama-quantize.exe in a Linux container, which is unusual. The comment mentions matching Windows naming convention, suggesting the calling code expects this exact filename.
Verify whether training_model code hardcodes the .exe extension or if this can be made platform-aware:
🏁 Script executed:
#!/bin/bash
# Check for hardcoded .exe references in training code
rg -n "llama-quantize\.exe" training_model/Length of output: 218
I need to examine the broader context around these hardcoded references to see if there's platform detection logic or if the parameter can be overridden:
</parameter>
</invoke>
---
🏁 Script executed:
```shell
#!/bin/bash
# Check context around the hardcoded .exe references
rg -n -B5 -A5 "llama-quantize\.exe" training_model/one_file_train.py
Length of output: 924
🏁 Script executed:
#!/bin/bash
# Check function signature and how quantized_path is used
ast-grep --pattern $'def $_($$$quantized_path$$$) {
$$$
}'Length of output: 0
🏁 Script executed:
#!/bin/bash
# Search for platform detection or conditional logic related to quantize
rg -n -i "platform|windows|linux|os\." training_model/one_file_train.py | head -20Length of output: 1039
I need to check how this function is called to see if the parameter is being overridden or if it relies on the default:
</parameter>
</invoke>
---
🏁 Script executed:
```shell
#!/bin/bash
# Find all calls to quantize_model function
rg -n "quantize_model\(" training_model/ --type=py
Length of output: 138
🏁 Script executed:
#!/bin/bash
# Check if there are any tests or other references to the quantized_path parameter
rg -n "quantized_path\s*=" training_model/ --type=pyLength of output: 102
🏁 Script executed:
#!/bin/bash
# Look for platform detection at top level or imports
head -50 training_model/one_file_train.py | grep -n "import\|platform\|sys"Length of output: 906
I need to check what the config actually contains for the quantized_path value:
</parameter>
</invoke>
---
🏁 Script executed:
```shell
#!/bin/bash
# Find config structure definition
rg -n "quantized_path" training_model/ --type=py -B2 -A2
Length of output: 1646
🏁 Script executed:
#!/bin/bash
# Look for config file or config defaults
fd -type f -name "*.yaml" -o -name "*.yml" -o -name "*config*" training_model/ 2>/dev/null | head -10Length of output: 0
🏁 Script executed:
#!/bin/bash
# Check context around line 1836-1841 to see full function call
sed -n '1830,1845p' training_model/one_file_train.pyLength of output: 761
Let me search for the config definition at the project level:
</parameter>
</invoke>
---
🏁 Script executed:
```shell
#!/bin/bash
# Search for config.yaml or similar at project root
find . -maxdepth 2 -type f \( -name "*.yaml" -o -name "*.yml" -o -name "config.py" \) 2>/dev/null | head -20
Length of output: 105
🏁 Script executed:
#!/bin/bash
# Search for where cfg.paths.quantized_path is defined
rg -n "quantized_path.*=" . --type=py --type=yaml | grep -v "^training_model"Length of output: 383
🏁 Script executed:
#!/bin/bash
# Look in parent dirs and common config locations
rg -n "paths:" . --type=yaml -A5 | head -30Length of output: 313
</parameter>
</invoke>
---
🏁 Script executed:
```shell
#!/bin/bash
# Read the full paths section from config.yaml
sed -n '163,180p' ./conf/config.yaml
Length of output: 604
🏁 Script executed:
#!/bin/bash
# Look for quantized_path in entire config.yaml
cat ./conf/config.yaml | grep -n "quantized_path" -B2 -A2Length of output: 268
Fix hardcoded .exe extension in error message and config assumptions.
The .exe extension is hardcoded in two places with design issues:
-
conf/config.yaml (line 175): Config sets
quantized_path: "llama-quantize.exe", but the commented alternative on line 174 shows platform variations are expected. Tests (test_training_functions.py lines 302, 327) use the platform-neutral"llama-quantize"without.exe, creating inconsistency. -
training_model/one_file_train.py (line 1709): Error message hardcodes
"llama-quantize.exe"as a string literal, but the actual executable path comes from thequantized_pathparameter. If the config ever changes, the error message will be misleading.
The Dockerfile symlink on line 58 is a workaround for these hardcoded assumptions. Either make the naming truly platform-aware throughout or document that .exe is required specifically for the Docker environment.
🤖 Prompt for AI Agents
Dockerfile lines 57-58: the symlink creation is compensating for a hardcoded
".exe" assumption elsewhere; remove the workaround by making the executable name
platform-neutral across the codebase: set conf/config.yaml quantized_path to
"llama-quantize" (or derive it at runtime based on platform), update
training_model/one_file_train.py to reference the quantized_path parameter in
its error message instead of the literal "llama-quantize.exe", and update tests
to use the platform-neutral name; alternatively, if Docker must provide a
Windows-named binary, change the Dockerfile to create both names (llama-quantize
and llama-quantize.exe) or document the requirement—pick one approach and make
the changes consistently.
| * **Game-specific Evaluation**: Specialized metrics for conversational AI and game-based interactions | ||
| * **Model Performance Assessment**: Comprehensive evaluation of fine-tuned models across multiple dimensions | ||
| * **Pytest Integration**: Structured testing framework with unit and integration test separation | ||
| * **Evaluation Metrics**: Support for BLEU, ROGUE, perplexity, and custom domain-specific metrics |
There was a problem hiding this comment.
Fix typo: "ROGUE" should be "ROUGE"
The metric name is misspelled. It should be "ROUGE" (Recall-Oriented Understudy for Gisting Evaluation), not "ROGUE".
Apply this diff:
-* **Evaluation Metrics**: Support for BLEU, ROGUE, perplexity, and custom domain-specific metrics
+* **Evaluation Metrics**: Support for BLEU, ROUGE, perplexity, and custom domain-specific metrics📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| * **Evaluation Metrics**: Support for BLEU, ROGUE, perplexity, and custom domain-specific metrics | |
| * **Evaluation Metrics**: Support for BLEU, ROUGE, perplexity, and custom domain-specific metrics |
🤖 Prompt for AI Agents
In docs/evaluation.rst around line 15, the metric name "ROGUE" is misspelled;
replace "ROGUE" with the correct "ROUGE" in the bullet text so the line reads "*
**Evaluation Metrics**: Support for BLEU, ROUGE, perplexity, and custom
domain-specific metrics".
| response = client.generate( | ||
| model=model_name, | ||
| prompt=prompt, | ||
| format=schema, | ||
| ) | ||
| logger = logging.getLogger(__name__) | ||
| logger.debug(response) | ||
| return response["response"] | ||
|
|
There was a problem hiding this comment.
Return a JSON string from ollama_generate.
ollama_generate currently returns the raw response["response"], which is a dict when using structured outputs. Downstream checks (e.g., test_actions) call json.loads(model_answer), so passing a dict triggers a TypeError and every Ollama-backed test fails. Serialize the structured response to a string (or otherwise normalize both code paths to return the same type). Example fix:
- logger.debug(response)
- return response["response"]
+ logger.debug(response)
+ parsed = response["response"]
+ if isinstance(parsed, (dict, list)):
+ return json.dumps(parsed, ensure_ascii=False)
+ return str(parsed)🤖 Prompt for AI Agents
In evaluation/model_evaluation.py around lines 140 to 148, ollama_generate
returns response["response"] which is a dict for structured outputs causing
downstream json.loads() callers to fail; ensure the function always returns a
JSON string by detecting if response["response"] is not a string and serializing
it (e.g., json.dumps(response["response"])) before returning, and add an import
json at the top if missing so both structured and plain-text paths return the
same type.
| print(f"1. Dataset size: {len(examples)} examples") | ||
| print(" Target: ~300-330 examples") | ||
| print(f" Status: {'PASS' if 250 <= len(examples) <= 350 else 'FAIL'}") | ||
|
|
||
| # 2. Conservative bias | ||
| razgovor_pct = (action_counts.get("Разговор", 0) / len(examples)) * 100 | ||
| print(f"\n2. Conservative 'Разговор' bias: {razgovor_pct:.1f}%") | ||
| print(" Target: 60-65%") | ||
| print( | ||
| f" Status: {'PASS' if 55 <= razgovor_pct <= 70 else 'ACCEPTABLE' if 50 <= razgovor_pct <= 75 else 'FAIL'}" |
There was a problem hiding this comment.
Guard against empty dataset before computing percentages
If the dataset loads but contains zero examples, the division on len(examples) raises ZeroDivisionError, terminating the validation run instead of reporting a failed check. Add an explicit empty-data guard before computing percentages.
-print(f"1. Dataset size: {len(examples)} examples")
+total_examples = len(examples)
+print(f"1. Dataset size: {total_examples} examples")
print(" Target: ~300-330 examples")
-print(f" Status: {'PASS' if 250 <= len(examples) <= 350 else 'FAIL'}")
+if total_examples == 0:
+ print(" Status: FAIL (dataset has no examples)")
+ sys.exit(1)
+print(f" Status: {'PASS' if 250 <= total_examples <= 350 else 'FAIL'}")
@@
-razgovor_pct = (action_counts.get("Разговор", 0) / len(examples)) * 100
+razgovor_pct = (action_counts.get("Разговор", 0) / total_examples) * 100Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In tools/dataset/validate_extended_dataset.py around lines 31 to 40, the script
computes percentages using len(examples) without checking for an empty dataset
which can raise ZeroDivisionError; add an explicit guard that detects
len(examples) == 0 and handles it (log or print a clear "FAIL" status for size
and skip or set percentages to 0/NA) before any division, then proceed to
compute razgovor_pct only when examples > 0 so the validation run completes
gracefully for empty datasets.
| # Use current working directory since get_original_cwd() requires Hydra decorator | ||
| data_dir = Path.cwd() / self.cfg.paths.data_dir | ||
| main_train(data_dir, self.cfg) |
There was a problem hiding this comment.
Data directory resolution inconsistent with main.py.
This uses Path.cwd() / cfg.paths.data_dir which assumes the current working directory is the project root. However, training_model/__main__.py (lines 37-48) implements more robust absolute/relative path handling.
Consider extracting the path resolution logic into a shared helper:
def resolve_data_dir(cfg: DictConfig) -> Path:
"""Resolve data directory from config, handling absolute and relative paths."""
data_dir_config = Path(cfg.paths.data_dir)
if data_dir_config.is_absolute():
return data_dir_config
# Resolve relative to project root
# Could use marker files like pyproject.toml instead of hardcoded parents
return Path.cwd() / data_dir_configThen use it consistently in both __main__.py and the CLI methods.
🤖 Prompt for AI Agents
In training_model/__init__.py around lines 118 to 120, the data directory is
resolved with Path.cwd() / cfg.paths.data_dir which is inconsistent with the
more robust handling in training_model/__main__.py; extract the resolution logic
into a shared helper (e.g. resolve_data_dir(cfg) that accepts the config, treats
absolute paths as-is and resolves relative paths against the project root or a
chosen marker file), replace the direct Path.cwd() usage with a call to that
helper, and update __main__.py and any CLI entrypoints to use the same helper so
path resolution is consistent across the package.
| work_dir = Path.cwd() | ||
| data_dir = Path(work_dir) / cfg.paths.data_dir | ||
| train_file = data_dir / cfg.dpo.train_data | ||
| val_file = data_dir / cfg.dpo.val_data | ||
|
|
||
| if not train_file.exists(): | ||
| logger.error(f"DPO training data file not found: {train_file}") | ||
| return False | ||
|
|
||
| if not val_file.exists(): | ||
| logger.error(f"DPO validation data file not found: {val_file}") | ||
| return False | ||
|
|
||
| beta = getattr(cfg.dpo, "beta", None) | ||
| if beta is not None and beta <= 0: | ||
| logger.error("DPO beta parameter must be positive") | ||
| return False | ||
|
|
||
| max_length = getattr(cfg.dpo, "max_length", None) | ||
| if max_length is not None and max_length <= 0: | ||
| logger.error("DPO max_length must be positive") | ||
| return False | ||
|
|
||
| logger.info("DPO configuration validation passed") | ||
| return True | ||
|
|
||
|
|
||
| def validate_grpo_config(cfg: DictConfig) -> bool: | ||
| """Validate GRPO configuration parameters. | ||
|
|
||
| Args: | ||
| cfg: Configuration object | ||
|
|
||
| Returns: | ||
| bool: True if configuration is valid | ||
| """ | ||
| logger = logging.getLogger(__name__) | ||
|
|
||
| if not hasattr(cfg, "grpo"): | ||
| logger.info("GRPO configuration not found - skipping GRPO validation") | ||
| return True | ||
|
|
||
| if not hasattr(cfg, "paths") or cfg.paths is None: | ||
| logger.error("Missing required GRPO parameters or cfg.paths: [paths section missing]") | ||
| return False | ||
|
|
||
| # Check GRPO-specific parameters in cfg.grpo | ||
| grpo_params = ["num_generations", "val_data", "train_data"] | ||
| missing_grpo_params = [p for p in grpo_params if not hasattr(cfg.grpo, p)] | ||
|
|
||
| # Check path-based parameters in cfg.paths | ||
| path_params = ["data_dir"] | ||
| missing_path_params = [p for p in path_params if getattr(cfg.paths, p, None) is None] | ||
|
|
||
| all_missing_params = [] | ||
| if missing_grpo_params: | ||
| all_missing_params.extend([f"grpo.{p}" for p in missing_grpo_params]) | ||
| if missing_path_params: | ||
| all_missing_params.extend([f"paths.{p}" for p in missing_path_params]) | ||
|
|
||
| if all_missing_params: | ||
| logger.error(f"Missing required GRPO parameters or cfg.paths: {all_missing_params}") | ||
| return False | ||
|
|
||
| work_dir = Path.cwd() | ||
| data_dir = Path(work_dir) / cfg.paths.data_dir | ||
| train_file = data_dir / cfg.grpo.train_data | ||
| val_file = data_dir / cfg.grpo.val_data | ||
|
|
||
| if not train_file.exists(): | ||
| logger.error(f"GRPO training data file not found: {train_file}") | ||
| return False | ||
|
|
||
| if not val_file.exists(): | ||
| logger.error(f"GRPO validation data file not found: {val_file}") | ||
| return False | ||
|
|
||
| num_generations = getattr(cfg.grpo, "num_generations", None) | ||
| if num_generations is not None and num_generations <= 0: | ||
| logger.error("GRPO num_generations must be positive") | ||
| return False | ||
|
|
||
| epsilon = getattr(cfg.grpo, "epsilon", None) | ||
| if epsilon is not None and (epsilon <= 0 or epsilon > 1): | ||
| logger.error("GRPO epsilon must be between 0 and 1") | ||
| return False | ||
|
|
||
| temperature = getattr(cfg.grpo, "temperature", None) | ||
| if temperature is not None and temperature <= 0: | ||
| logger.error("GRPO temperature must be positive") |
There was a problem hiding this comment.
Use Hydra’s original cwd when validating DPO/GRPO paths.
These validators still rely on Path.cwd(), so under Hydra they scan the transient output directory and falsely report missing datasets. Reuse the same get_original_cwd() fallback logic you already added in validate_paths_config for both DPO and GRPO checks.
- work_dir = Path.cwd()
- data_dir = Path(work_dir) / cfg.paths.data_dir
+ try:
+ from hydra.core.hydra_config import HydraConfig
+ from hydra.utils import get_original_cwd
+
+ if HydraConfig.initialized():
+ base_dir = Path(get_original_cwd())
+ else:
+ base_dir = Path.cwd()
+ except (ImportError, ValueError):
+ base_dir = Path.cwd()
+
+ data_dir = base_dir / cfg.paths.data_dir
@@
- work_dir = Path.cwd()
- data_dir = Path(work_dir) / cfg.paths.data_dir
+ try:
+ from hydra.core.hydra_config import HydraConfig
+ from hydra.utils import get_original_cwd
+
+ if HydraConfig.initialized():
+ base_dir = Path(get_original_cwd())
+ else:
+ base_dir = Path.cwd()
+ except (ImportError, ValueError):
+ base_dir = Path.cwd()
+
+ data_dir = base_dir / cfg.paths.data_dir📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| work_dir = Path.cwd() | |
| data_dir = Path(work_dir) / cfg.paths.data_dir | |
| train_file = data_dir / cfg.dpo.train_data | |
| val_file = data_dir / cfg.dpo.val_data | |
| if not train_file.exists(): | |
| logger.error(f"DPO training data file not found: {train_file}") | |
| return False | |
| if not val_file.exists(): | |
| logger.error(f"DPO validation data file not found: {val_file}") | |
| return False | |
| beta = getattr(cfg.dpo, "beta", None) | |
| if beta is not None and beta <= 0: | |
| logger.error("DPO beta parameter must be positive") | |
| return False | |
| max_length = getattr(cfg.dpo, "max_length", None) | |
| if max_length is not None and max_length <= 0: | |
| logger.error("DPO max_length must be positive") | |
| return False | |
| logger.info("DPO configuration validation passed") | |
| return True | |
| def validate_grpo_config(cfg: DictConfig) -> bool: | |
| """Validate GRPO configuration parameters. | |
| Args: | |
| cfg: Configuration object | |
| Returns: | |
| bool: True if configuration is valid | |
| """ | |
| logger = logging.getLogger(__name__) | |
| if not hasattr(cfg, "grpo"): | |
| logger.info("GRPO configuration not found - skipping GRPO validation") | |
| return True | |
| if not hasattr(cfg, "paths") or cfg.paths is None: | |
| logger.error("Missing required GRPO parameters or cfg.paths: [paths section missing]") | |
| return False | |
| # Check GRPO-specific parameters in cfg.grpo | |
| grpo_params = ["num_generations", "val_data", "train_data"] | |
| missing_grpo_params = [p for p in grpo_params if not hasattr(cfg.grpo, p)] | |
| # Check path-based parameters in cfg.paths | |
| path_params = ["data_dir"] | |
| missing_path_params = [p for p in path_params if getattr(cfg.paths, p, None) is None] | |
| all_missing_params = [] | |
| if missing_grpo_params: | |
| all_missing_params.extend([f"grpo.{p}" for p in missing_grpo_params]) | |
| if missing_path_params: | |
| all_missing_params.extend([f"paths.{p}" for p in missing_path_params]) | |
| if all_missing_params: | |
| logger.error(f"Missing required GRPO parameters or cfg.paths: {all_missing_params}") | |
| return False | |
| work_dir = Path.cwd() | |
| data_dir = Path(work_dir) / cfg.paths.data_dir | |
| train_file = data_dir / cfg.grpo.train_data | |
| val_file = data_dir / cfg.grpo.val_data | |
| if not train_file.exists(): | |
| logger.error(f"GRPO training data file not found: {train_file}") | |
| return False | |
| if not val_file.exists(): | |
| logger.error(f"GRPO validation data file not found: {val_file}") | |
| return False | |
| num_generations = getattr(cfg.grpo, "num_generations", None) | |
| if num_generations is not None and num_generations <= 0: | |
| logger.error("GRPO num_generations must be positive") | |
| return False | |
| epsilon = getattr(cfg.grpo, "epsilon", None) | |
| if epsilon is not None and (epsilon <= 0 or epsilon > 1): | |
| logger.error("GRPO epsilon must be between 0 and 1") | |
| return False | |
| temperature = getattr(cfg.grpo, "temperature", None) | |
| if temperature is not None and temperature <= 0: | |
| logger.error("GRPO temperature must be positive") | |
| try: | |
| from hydra.core.hydra_config import HydraConfig | |
| from hydra.utils import get_original_cwd | |
| if HydraConfig.initialized(): | |
| base_dir = Path(get_original_cwd()) | |
| else: | |
| base_dir = Path.cwd() | |
| except (ImportError, ValueError): | |
| base_dir = Path.cwd() | |
| data_dir = base_dir / cfg.paths.data_dir | |
| train_file = data_dir / cfg.dpo.train_data | |
| val_file = data_dir / cfg.dpo.val_data | |
| if not train_file.exists(): | |
| logger.error(f"DPO training data file not found: {train_file}") | |
| return False | |
| if not val_file.exists(): | |
| logger.error(f"DPO validation data file not found: {val_file}") | |
| return False | |
| beta = getattr(cfg.dpo, "beta", None) | |
| if beta is not None and beta <= 0: | |
| logger.error("DPO beta parameter must be positive") | |
| return False | |
| max_length = getattr(cfg.dpo, "max_length", None) | |
| if max_length is not None and max_length <= 0: | |
| logger.error("DPO max_length must be positive") | |
| return False | |
| logger.info("DPO configuration validation passed") | |
| return True | |
| def validate_grpo_config(cfg: DictConfig) -> bool: | |
| """Validate GRPO configuration parameters. | |
| Args: | |
| cfg: Configuration object | |
| Returns: | |
| bool: True if configuration is valid | |
| """ | |
| logger = logging.getLogger(__name__) | |
| if not hasattr(cfg, "grpo"): | |
| logger.info("GRPO configuration not found - skipping GRPO validation") | |
| return True | |
| if not hasattr(cfg, "paths") or cfg.paths is None: | |
| logger.error("Missing required GRPO parameters or cfg.paths: [paths section missing]") | |
| return False | |
| # Check GRPO-specific parameters in cfg.grpo | |
| grpo_params = ["num_generations", "val_data", "train_data"] | |
| missing_grpo_params = [p for p in grpo_params if not hasattr(cfg.grpo, p)] | |
| # Check path-based parameters in cfg.paths | |
| path_params = ["data_dir"] | |
| missing_path_params = [p for p in path_params if getattr(cfg.paths, p, None) is None] | |
| all_missing_params = [] | |
| if missing_grpo_params: | |
| all_missing_params.extend([f"grpo.{p}" for p in missing_grpo_params]) | |
| if missing_path_params: | |
| all_missing_params.extend([f"paths.{p}" for p in missing_path_params]) | |
| if all_missing_params: | |
| logger.error(f"Missing required GRPO parameters or cfg.paths: {all_missing_params}") | |
| return False | |
| try: | |
| from hydra.core.hydra_config import HydraConfig | |
| from hydra.utils import get_original_cwd | |
| if HydraConfig.initialized(): | |
| base_dir = Path(get_original_cwd()) | |
| else: | |
| base_dir = Path.cwd() | |
| except (ImportError, ValueError): | |
| base_dir = Path.cwd() | |
| data_dir = base_dir / cfg.paths.data_dir | |
| train_file = data_dir / cfg.grpo.train_data | |
| val_file = data_dir / cfg.grpo.val_data | |
| if not train_file.exists(): | |
| logger.error(f"GRPO training data file not found: {train_file}") | |
| return False | |
| if not val_file.exists(): | |
| logger.error(f"GRPO validation data file not found: {val_file}") | |
| return False | |
| num_generations = getattr(cfg.grpo, "num_generations", None) | |
| if num_generations is not None and num_generations <= 0: | |
| logger.error("GRPO num_generations must be positive") | |
| return False | |
| epsilon = getattr(cfg.grpo, "epsilon", None) | |
| if epsilon is not None and (epsilon <= 0 or epsilon > 1): | |
| logger.error("GRPO epsilon must be between 0 and 1") | |
| return False | |
| temperature = getattr(cfg.grpo, "temperature", None) | |
| if temperature is not None and temperature <= 0: | |
| logger.error("GRPO temperature must be positive") |
🤖 Prompt for AI Agents
In training_model/config_validation.py around lines 215 to 304, the DPO/GRPO
validators use Path.cwd() which under Hydra points to the transient output dir
and causes false missing-file errors; change the work_dir calculation to reuse
the same get_original_cwd() fallback logic used in validate_paths_config (i.e.,
call get_original_cwd() if available and fall back to Path.cwd()), update both
the DPO and GRPO blocks to compute data_dir from that work_dir, and ensure
get_original_cwd is imported or accessible in this file.
| dataset_logging_cfg = getattr(cfg.logging, "dataset_logging", {}) | ||
| if not getattr(dataset_logging_cfg, "enabled", False): | ||
| return | ||
|
|
||
| # Get logging configuration parameters | ||
| log_level = getattr(dataset_logging_cfg, "log_level", "INFO").upper() | ||
| num_samples = getattr(dataset_logging_cfg, "num_samples", 5) |
There was a problem hiding this comment.
Guard dataset logging when no logging block is provided.
Accessing cfg.logging directly raises an AttributeError for minimal configs, which crashes training the first time log_dataset_samples runs. Please guard the lookup so the helper becomes a no-op when the logging section is absent.
- dataset_logging_cfg = getattr(cfg.logging, "dataset_logging", {})
+ logging_cfg = getattr(cfg, "logging", None)
+ dataset_logging_cfg = (
+ getattr(logging_cfg, "dataset_logging", {}) if logging_cfg else {}
+ )🤖 Prompt for AI Agents
In training_model/logging_utils.py around lines 279 to 285, the code accesses
cfg.logging directly which raises AttributeError when the config omits the
logging block; change the guard to first check for the logging section (e.g.,
use getattr(cfg, "logging", None) or getattr(cfg, "logging", {}) ) and if it's
missing or falsy return early so the function becomes a no-op when there is no
logging section; then proceed to read dataset_logging via getattr on that safe
value and continue as before.
- conf/config.yaml: set mlflow tracking_uri to https://mlflow.komolov.synology.me - testing_model/vllm_test.py: extract SYSTEM_PROMPT constant (E501 line-too-long) - .readthedocs.yaml: fix end-of-file and trailing whitespace - .gitignore: add gptchain/ and db/
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@testing_model/vllm_test.py`:
- Around line 9-17: The SYSTEM_PROMPT defines the contract asking the model to
return JSON with an 'Actions' key, but the evaluator code at line 65 reads from
'Content.Action' (singular), causing a mismatch that leads to KeyError or
scoring failures when the model follows the prompt. Align the key name between
the SYSTEM_PROMPT (lines 9-17) and the evaluator code at line 65 so they both
reference the same key, and add guard/error handling around the JSON key access
to safely handle cases where the expected keys are missing or misnamed.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 8f42fb8d-be2b-4d56-826e-e97ec3add46a
📒 Files selected for processing (4)
.gitignore.readthedocs.yamlconf/config.yamltesting_model/vllm_test.py
✅ Files skipped from review due to trivial changes (1)
- .gitignore
| SYSTEM_PROMPT = ( | ||
| "Ты – помощник по имени ВИКА на заброшенной космической станции. " | ||
| "У тебя есть доступ к системам станции. Отвечай только в формате JSON " | ||
| "с ключами 'MessageText' и 'Actions', содержащими как минимум одно " | ||
| "(или несколько) доступных вам действий. Если в Actions есть имя действия, " | ||
| "оно будет исполнено. Заканчивайте ответ символом }. " | ||
| "Ниже – история сообщений из предыдущего диалога с пользователем, " | ||
| "а также список доступных тебе действий." | ||
| ) |
There was a problem hiding this comment.
Align action key contract between prompt and evaluator, and guard JSON parsing.
Line 12 asks the model for Actions, but Line 65 reads Content.Action. This mismatch can trigger KeyError or systematic mis-scoring when the model follows the prompt.
Suggested fix
SYSTEM_PROMPT = (
@@
- "с ключами 'MessageText' и 'Actions', содержащими как минимум одно "
+ "с ключами 'MessageText' и 'Action', содержащими как минимум одно "
@@
- if json.loads(response[0].outputs[0].text)["Content"]["Action"] == answers[number]:
+ try:
+ payload = json.loads(response[0].outputs[0].text)
+ predicted_action = payload.get("Content", {}).get("Action")
+ except (json.JSONDecodeError, TypeError):
+ predicted_action = None
+ if predicted_action == answers[number]:Also applies to: 65-65
🧰 Tools
🪛 Ruff (0.15.15)
[warning] 10-10: String contains ambiguous – (EN DASH). Did you mean - (HYPHEN-MINUS)?
(RUF001)
[warning] 11-11: String contains ambiguous У (CYRILLIC CAPITAL LETTER U). Did you mean Y (LATIN CAPITAL LETTER Y)?
(RUF001)
[warning] 12-12: String contains ambiguous с (CYRILLIC SMALL LETTER ES). Did you mean c (LATIN SMALL LETTER C)?
(RUF001)
[warning] 15-15: String contains ambiguous – (EN DASH). Did you mean - (HYPHEN-MINUS)?
(RUF001)
[warning] 15-15: String contains ambiguous с (CYRILLIC SMALL LETTER ES). Did you mean c (LATIN SMALL LETTER C)?
(RUF001)
[warning] 16-16: String contains ambiguous а (CYRILLIC SMALL LETTER A). Did you mean a (LATIN SMALL LETTER A)?
(RUF001)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@testing_model/vllm_test.py` around lines 9 - 17, The SYSTEM_PROMPT defines
the contract asking the model to return JSON with an 'Actions' key, but the
evaluator code at line 65 reads from 'Content.Action' (singular), causing a
mismatch that leads to KeyError or scoring failures when the model follows the
prompt. Align the key name between the SYSTEM_PROMPT (lines 9-17) and the
evaluator code at line 65 so they both reference the same key, and add
guard/error handling around the JSON key access to safely handle cases where the
expected keys are missing or misnamed.
- pyproject.toml: remove B105 from bandit skips (unknown test on Python 3.12+, ast.Str removed) - training_model/one_file_train.py: fix I001 unsorted imports (move wandb into third-party block) - training_model/private_api.py: stop tracking (contains secrets, already in .gitignore)
A completely redesigned framework, many new features, improved quality of life, improved quality and reduced memory usage, fixed bugs, and more
Summary by CodeRabbit
New Features
Chores
Documentation