mcli-framework 7.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of mcli-framework might be problematic. Click here for more details.
- mcli/app/chat_cmd.py +42 -0
- mcli/app/commands_cmd.py +226 -0
- mcli/app/completion_cmd.py +216 -0
- mcli/app/completion_helpers.py +288 -0
- mcli/app/cron_test_cmd.py +697 -0
- mcli/app/logs_cmd.py +419 -0
- mcli/app/main.py +492 -0
- mcli/app/model/model.py +1060 -0
- mcli/app/model_cmd.py +227 -0
- mcli/app/redis_cmd.py +269 -0
- mcli/app/video/video.py +1114 -0
- mcli/app/visual_cmd.py +303 -0
- mcli/chat/chat.py +2409 -0
- mcli/chat/command_rag.py +514 -0
- mcli/chat/enhanced_chat.py +652 -0
- mcli/chat/system_controller.py +1010 -0
- mcli/chat/system_integration.py +1016 -0
- mcli/cli.py +25 -0
- mcli/config.toml +20 -0
- mcli/lib/api/api.py +586 -0
- mcli/lib/api/daemon_client.py +203 -0
- mcli/lib/api/daemon_client_local.py +44 -0
- mcli/lib/api/daemon_decorator.py +217 -0
- mcli/lib/api/mcli_decorators.py +1032 -0
- mcli/lib/auth/auth.py +85 -0
- mcli/lib/auth/aws_manager.py +85 -0
- mcli/lib/auth/azure_manager.py +91 -0
- mcli/lib/auth/credential_manager.py +192 -0
- mcli/lib/auth/gcp_manager.py +93 -0
- mcli/lib/auth/key_manager.py +117 -0
- mcli/lib/auth/mcli_manager.py +93 -0
- mcli/lib/auth/token_manager.py +75 -0
- mcli/lib/auth/token_util.py +1011 -0
- mcli/lib/config/config.py +47 -0
- mcli/lib/discovery/__init__.py +1 -0
- mcli/lib/discovery/command_discovery.py +274 -0
- mcli/lib/erd/erd.py +1345 -0
- mcli/lib/erd/generate_graph.py +453 -0
- mcli/lib/files/files.py +76 -0
- mcli/lib/fs/fs.py +109 -0
- mcli/lib/lib.py +29 -0
- mcli/lib/logger/logger.py +611 -0
- mcli/lib/performance/optimizer.py +409 -0
- mcli/lib/performance/rust_bridge.py +502 -0
- mcli/lib/performance/uvloop_config.py +154 -0
- mcli/lib/pickles/pickles.py +50 -0
- mcli/lib/search/cached_vectorizer.py +479 -0
- mcli/lib/services/data_pipeline.py +460 -0
- mcli/lib/services/lsh_client.py +441 -0
- mcli/lib/services/redis_service.py +387 -0
- mcli/lib/shell/shell.py +137 -0
- mcli/lib/toml/toml.py +33 -0
- mcli/lib/ui/styling.py +47 -0
- mcli/lib/ui/visual_effects.py +634 -0
- mcli/lib/watcher/watcher.py +185 -0
- mcli/ml/api/app.py +215 -0
- mcli/ml/api/middleware.py +224 -0
- mcli/ml/api/routers/admin_router.py +12 -0
- mcli/ml/api/routers/auth_router.py +244 -0
- mcli/ml/api/routers/backtest_router.py +12 -0
- mcli/ml/api/routers/data_router.py +12 -0
- mcli/ml/api/routers/model_router.py +302 -0
- mcli/ml/api/routers/monitoring_router.py +12 -0
- mcli/ml/api/routers/portfolio_router.py +12 -0
- mcli/ml/api/routers/prediction_router.py +267 -0
- mcli/ml/api/routers/trade_router.py +12 -0
- mcli/ml/api/routers/websocket_router.py +76 -0
- mcli/ml/api/schemas.py +64 -0
- mcli/ml/auth/auth_manager.py +425 -0
- mcli/ml/auth/models.py +154 -0
- mcli/ml/auth/permissions.py +302 -0
- mcli/ml/backtesting/backtest_engine.py +502 -0
- mcli/ml/backtesting/performance_metrics.py +393 -0
- mcli/ml/cache.py +400 -0
- mcli/ml/cli/main.py +398 -0
- mcli/ml/config/settings.py +394 -0
- mcli/ml/configs/dvc_config.py +230 -0
- mcli/ml/configs/mlflow_config.py +131 -0
- mcli/ml/configs/mlops_manager.py +293 -0
- mcli/ml/dashboard/app.py +532 -0
- mcli/ml/dashboard/app_integrated.py +738 -0
- mcli/ml/dashboard/app_supabase.py +560 -0
- mcli/ml/dashboard/app_training.py +615 -0
- mcli/ml/dashboard/cli.py +51 -0
- mcli/ml/data_ingestion/api_connectors.py +501 -0
- mcli/ml/data_ingestion/data_pipeline.py +567 -0
- mcli/ml/data_ingestion/stream_processor.py +512 -0
- mcli/ml/database/migrations/env.py +94 -0
- mcli/ml/database/models.py +667 -0
- mcli/ml/database/session.py +200 -0
- mcli/ml/experimentation/ab_testing.py +845 -0
- mcli/ml/features/ensemble_features.py +607 -0
- mcli/ml/features/political_features.py +676 -0
- mcli/ml/features/recommendation_engine.py +809 -0
- mcli/ml/features/stock_features.py +573 -0
- mcli/ml/features/test_feature_engineering.py +346 -0
- mcli/ml/logging.py +85 -0
- mcli/ml/mlops/data_versioning.py +518 -0
- mcli/ml/mlops/experiment_tracker.py +377 -0
- mcli/ml/mlops/model_serving.py +481 -0
- mcli/ml/mlops/pipeline_orchestrator.py +614 -0
- mcli/ml/models/base_models.py +324 -0
- mcli/ml/models/ensemble_models.py +675 -0
- mcli/ml/models/recommendation_models.py +474 -0
- mcli/ml/models/test_models.py +487 -0
- mcli/ml/monitoring/drift_detection.py +676 -0
- mcli/ml/monitoring/metrics.py +45 -0
- mcli/ml/optimization/portfolio_optimizer.py +834 -0
- mcli/ml/preprocessing/data_cleaners.py +451 -0
- mcli/ml/preprocessing/feature_extractors.py +491 -0
- mcli/ml/preprocessing/ml_pipeline.py +382 -0
- mcli/ml/preprocessing/politician_trading_preprocessor.py +569 -0
- mcli/ml/preprocessing/test_preprocessing.py +294 -0
- mcli/ml/scripts/populate_sample_data.py +200 -0
- mcli/ml/tasks.py +400 -0
- mcli/ml/tests/test_integration.py +429 -0
- mcli/ml/tests/test_training_dashboard.py +387 -0
- mcli/public/oi/oi.py +15 -0
- mcli/public/public.py +4 -0
- mcli/self/self_cmd.py +1246 -0
- mcli/workflow/daemon/api_daemon.py +800 -0
- mcli/workflow/daemon/async_command_database.py +681 -0
- mcli/workflow/daemon/async_process_manager.py +591 -0
- mcli/workflow/daemon/client.py +530 -0
- mcli/workflow/daemon/commands.py +1196 -0
- mcli/workflow/daemon/daemon.py +905 -0
- mcli/workflow/daemon/daemon_api.py +59 -0
- mcli/workflow/daemon/enhanced_daemon.py +571 -0
- mcli/workflow/daemon/process_cli.py +244 -0
- mcli/workflow/daemon/process_manager.py +439 -0
- mcli/workflow/daemon/test_daemon.py +275 -0
- mcli/workflow/dashboard/dashboard_cmd.py +113 -0
- mcli/workflow/docker/docker.py +0 -0
- mcli/workflow/file/file.py +100 -0
- mcli/workflow/gcloud/config.toml +21 -0
- mcli/workflow/gcloud/gcloud.py +58 -0
- mcli/workflow/git_commit/ai_service.py +328 -0
- mcli/workflow/git_commit/commands.py +430 -0
- mcli/workflow/lsh_integration.py +355 -0
- mcli/workflow/model_service/client.py +594 -0
- mcli/workflow/model_service/download_and_run_efficient_models.py +288 -0
- mcli/workflow/model_service/lightweight_embedder.py +397 -0
- mcli/workflow/model_service/lightweight_model_server.py +714 -0
- mcli/workflow/model_service/lightweight_test.py +241 -0
- mcli/workflow/model_service/model_service.py +1955 -0
- mcli/workflow/model_service/ollama_efficient_runner.py +425 -0
- mcli/workflow/model_service/pdf_processor.py +386 -0
- mcli/workflow/model_service/test_efficient_runner.py +234 -0
- mcli/workflow/model_service/test_example.py +315 -0
- mcli/workflow/model_service/test_integration.py +131 -0
- mcli/workflow/model_service/test_new_features.py +149 -0
- mcli/workflow/openai/openai.py +99 -0
- mcli/workflow/politician_trading/commands.py +1790 -0
- mcli/workflow/politician_trading/config.py +134 -0
- mcli/workflow/politician_trading/connectivity.py +490 -0
- mcli/workflow/politician_trading/data_sources.py +395 -0
- mcli/workflow/politician_trading/database.py +410 -0
- mcli/workflow/politician_trading/demo.py +248 -0
- mcli/workflow/politician_trading/models.py +165 -0
- mcli/workflow/politician_trading/monitoring.py +413 -0
- mcli/workflow/politician_trading/scrapers.py +966 -0
- mcli/workflow/politician_trading/scrapers_california.py +412 -0
- mcli/workflow/politician_trading/scrapers_eu.py +377 -0
- mcli/workflow/politician_trading/scrapers_uk.py +350 -0
- mcli/workflow/politician_trading/scrapers_us_states.py +438 -0
- mcli/workflow/politician_trading/supabase_functions.py +354 -0
- mcli/workflow/politician_trading/workflow.py +852 -0
- mcli/workflow/registry/registry.py +180 -0
- mcli/workflow/repo/repo.py +223 -0
- mcli/workflow/scheduler/commands.py +493 -0
- mcli/workflow/scheduler/cron_parser.py +238 -0
- mcli/workflow/scheduler/job.py +182 -0
- mcli/workflow/scheduler/monitor.py +139 -0
- mcli/workflow/scheduler/persistence.py +324 -0
- mcli/workflow/scheduler/scheduler.py +679 -0
- mcli/workflow/sync/sync_cmd.py +437 -0
- mcli/workflow/sync/test_cmd.py +314 -0
- mcli/workflow/videos/videos.py +242 -0
- mcli/workflow/wakatime/wakatime.py +11 -0
- mcli/workflow/workflow.py +37 -0
- mcli_framework-7.0.0.dist-info/METADATA +479 -0
- mcli_framework-7.0.0.dist-info/RECORD +186 -0
- mcli_framework-7.0.0.dist-info/WHEEL +5 -0
- mcli_framework-7.0.0.dist-info/entry_points.txt +7 -0
- mcli_framework-7.0.0.dist-info/licenses/LICENSE +21 -0
- mcli_framework-7.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Any, Dict, Optional
|
|
4
|
+
|
|
5
|
+
import ollama
|
|
6
|
+
|
|
7
|
+
from mcli.lib.logger.logger import get_logger
|
|
8
|
+
from mcli.lib.toml.toml import read_from_toml
|
|
9
|
+
|
|
10
|
+
logger = get_logger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class GitCommitAIService:
|
|
14
|
+
"""AI service for generating intelligent git commit messages"""
|
|
15
|
+
|
|
16
|
+
def __init__(self):
|
|
17
|
+
self.config = self._load_config()
|
|
18
|
+
self.model_name = self.config.get("model", "phi3-mini")
|
|
19
|
+
self.temperature = float(
|
|
20
|
+
self.config.get("temperature", 0.3)
|
|
21
|
+
) # Lower temp for more consistent commits
|
|
22
|
+
self.ollama_base_url = self.config.get("ollama_base_url", "http://localhost:11434")
|
|
23
|
+
|
|
24
|
+
def _load_config(self) -> Dict[str, Any]:
|
|
25
|
+
"""Load LLM configuration from config.toml"""
|
|
26
|
+
try:
|
|
27
|
+
config = read_from_toml("config.toml", "llm") or {}
|
|
28
|
+
|
|
29
|
+
if not config:
|
|
30
|
+
# Default configuration for git commits
|
|
31
|
+
config = {
|
|
32
|
+
"provider": "local",
|
|
33
|
+
"model": "phi3-mini",
|
|
34
|
+
"temperature": 0.3,
|
|
35
|
+
"ollama_base_url": "http://localhost:11434",
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return config
|
|
39
|
+
except Exception as e:
|
|
40
|
+
logger.warning(f"Could not load config, using defaults: {e}")
|
|
41
|
+
return {
|
|
42
|
+
"provider": "local",
|
|
43
|
+
"model": "phi3-mini",
|
|
44
|
+
"temperature": 0.3,
|
|
45
|
+
"ollama_base_url": "http://localhost:11434",
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
def _analyze_file_patterns(self, changes: Dict[str, Any]) -> Dict[str, Any]:
|
|
49
|
+
"""Analyze file patterns to understand the scope of changes"""
|
|
50
|
+
analysis = {"languages": set(), "categories": set(), "scope": "unknown", "confidence": 0.0}
|
|
51
|
+
|
|
52
|
+
all_files = (
|
|
53
|
+
changes["changes"]["added"]
|
|
54
|
+
+ changes["changes"]["modified"]
|
|
55
|
+
+ changes["changes"]["deleted"]
|
|
56
|
+
+ changes["changes"]["renamed"]
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# Language detection
|
|
60
|
+
language_map = {
|
|
61
|
+
".py": "python",
|
|
62
|
+
".js": "javascript",
|
|
63
|
+
".ts": "typescript",
|
|
64
|
+
".jsx": "react",
|
|
65
|
+
".tsx": "react",
|
|
66
|
+
".go": "go",
|
|
67
|
+
".rs": "rust",
|
|
68
|
+
".java": "java",
|
|
69
|
+
".cpp": "cpp",
|
|
70
|
+
".c": "c",
|
|
71
|
+
".html": "html",
|
|
72
|
+
".css": "css",
|
|
73
|
+
".scss": "sass",
|
|
74
|
+
".md": "markdown",
|
|
75
|
+
".yml": "yaml",
|
|
76
|
+
".yaml": "yaml",
|
|
77
|
+
".json": "json",
|
|
78
|
+
".toml": "toml",
|
|
79
|
+
".dockerfile": "docker",
|
|
80
|
+
".sql": "sql",
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
# Category detection
|
|
84
|
+
category_patterns = {
|
|
85
|
+
"tests": ["test_", "_test", "tests/", "/test/", ".test.", "spec_", "_spec"],
|
|
86
|
+
"docs": [".md", "README", "CHANGELOG", "docs/", "/doc/"],
|
|
87
|
+
"config": [".toml", ".yml", ".yaml", ".json", "config", ".env", "settings"],
|
|
88
|
+
"build": [
|
|
89
|
+
"Dockerfile",
|
|
90
|
+
"requirements",
|
|
91
|
+
"package.json",
|
|
92
|
+
"cargo.toml",
|
|
93
|
+
"pom.xml",
|
|
94
|
+
"build",
|
|
95
|
+
"makefile",
|
|
96
|
+
],
|
|
97
|
+
"frontend": [
|
|
98
|
+
".html",
|
|
99
|
+
".css",
|
|
100
|
+
".scss",
|
|
101
|
+
".js",
|
|
102
|
+
".jsx",
|
|
103
|
+
".ts",
|
|
104
|
+
".tsx",
|
|
105
|
+
"assets/",
|
|
106
|
+
"static/",
|
|
107
|
+
],
|
|
108
|
+
"backend": [".py", ".go", ".rs", ".java", ".cpp", "api/", "server/", "backend/"],
|
|
109
|
+
"database": [".sql", "migration", "schema", "models/", "db/"],
|
|
110
|
+
"infra": ["docker", "deploy", "infrastructure", ".tf", "kubernetes", "helm"],
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
for file in all_files:
|
|
114
|
+
file_lower = file.lower()
|
|
115
|
+
|
|
116
|
+
# Detect languages
|
|
117
|
+
for ext, lang in language_map.items():
|
|
118
|
+
if file_lower.endswith(ext):
|
|
119
|
+
analysis["languages"].add(lang)
|
|
120
|
+
break
|
|
121
|
+
|
|
122
|
+
# Detect categories
|
|
123
|
+
for category, patterns in category_patterns.items():
|
|
124
|
+
if any(pattern in file_lower for pattern in patterns):
|
|
125
|
+
analysis["categories"].add(category)
|
|
126
|
+
|
|
127
|
+
# Determine scope
|
|
128
|
+
if len(all_files) == 1:
|
|
129
|
+
analysis["scope"] = "single"
|
|
130
|
+
analysis["confidence"] = 0.9
|
|
131
|
+
elif len(all_files) <= 5:
|
|
132
|
+
analysis["scope"] = "focused"
|
|
133
|
+
analysis["confidence"] = 0.8
|
|
134
|
+
elif len(all_files) <= 15:
|
|
135
|
+
analysis["scope"] = "moderate"
|
|
136
|
+
analysis["confidence"] = 0.7
|
|
137
|
+
else:
|
|
138
|
+
analysis["scope"] = "broad"
|
|
139
|
+
analysis["confidence"] = 0.6
|
|
140
|
+
|
|
141
|
+
# Convert sets to lists for JSON serialization
|
|
142
|
+
analysis["languages"] = list(analysis["languages"])
|
|
143
|
+
analysis["categories"] = list(analysis["categories"])
|
|
144
|
+
|
|
145
|
+
return analysis
|
|
146
|
+
|
|
147
|
+
def _create_commit_prompt(
|
|
148
|
+
self, changes: Dict[str, Any], diff_content: str, analysis: Dict[str, Any]
|
|
149
|
+
) -> str:
|
|
150
|
+
"""Create a detailed prompt for AI commit message generation"""
|
|
151
|
+
|
|
152
|
+
# Truncate diff if too long (keep first 2000 chars)
|
|
153
|
+
truncated_diff = diff_content[:2000] + "..." if len(diff_content) > 2000 else diff_content
|
|
154
|
+
|
|
155
|
+
prompt = f"""You are an expert software developer writing git commit messages following conventional commit standards.
|
|
156
|
+
|
|
157
|
+
CHANGE ANALYSIS:
|
|
158
|
+
- Files changed: {changes['total_files']}
|
|
159
|
+
- New files: {len(changes['changes']['added'])}
|
|
160
|
+
- Modified files: {len(changes['changes']['modified'])}
|
|
161
|
+
- Deleted files: {len(changes['changes']['deleted'])}
|
|
162
|
+
- Languages: {', '.join(analysis['languages']) if analysis['languages'] else 'unknown'}
|
|
163
|
+
- Categories: {', '.join(analysis['categories']) if analysis['categories'] else 'general'}
|
|
164
|
+
- Scope: {analysis['scope']}
|
|
165
|
+
|
|
166
|
+
FILE DETAILS:
|
|
167
|
+
Added: {', '.join(changes['changes']['added'][:10])}{'...' if len(changes['changes']['added']) > 10 else ''}
|
|
168
|
+
Modified: {', '.join(changes['changes']['modified'][:10])}{'...' if len(changes['changes']['modified']) > 10 else ''}
|
|
169
|
+
Deleted: {', '.join(changes['changes']['deleted'][:10])}{'...' if len(changes['changes']['deleted']) > 10 else ''}
|
|
170
|
+
|
|
171
|
+
CODE DIFF SAMPLE:
|
|
172
|
+
{truncated_diff}
|
|
173
|
+
|
|
174
|
+
Generate a conventional commit message following this format:
|
|
175
|
+
<type>[optional scope]: <description>
|
|
176
|
+
|
|
177
|
+
[optional body]
|
|
178
|
+
|
|
179
|
+
Types: feat, fix, docs, style, refactor, test, chore, build, ci, perf
|
|
180
|
+
Scope: component/module affected (optional)
|
|
181
|
+
Description: imperative, lowercase, no period, max 50 chars
|
|
182
|
+
Body: explain what and why, not how (optional, max 72 chars per line)
|
|
183
|
+
|
|
184
|
+
Guidelines:
|
|
185
|
+
1. Use imperative mood ("add" not "added" or "adds")
|
|
186
|
+
2. Be specific but concise
|
|
187
|
+
3. Focus on WHAT changed and WHY
|
|
188
|
+
4. Don't describe HOW (that's in the diff)
|
|
189
|
+
5. Use conventional commit types appropriately
|
|
190
|
+
6. Add scope when changes are focused on specific area
|
|
191
|
+
7. Include body only if necessary for context
|
|
192
|
+
|
|
193
|
+
Examples:
|
|
194
|
+
- "feat(auth): add JWT token validation"
|
|
195
|
+
- "fix: resolve memory leak in user session cleanup"
|
|
196
|
+
- "docs: update API documentation for new endpoints"
|
|
197
|
+
- "refactor(database): optimize query performance"
|
|
198
|
+
- "test: add unit tests for payment processing"
|
|
199
|
+
|
|
200
|
+
Generate ONLY the commit message, nothing else:"""
|
|
201
|
+
|
|
202
|
+
return prompt
|
|
203
|
+
|
|
204
|
+
def generate_commit_message(self, changes: Dict[str, Any], diff_content: str) -> str:
|
|
205
|
+
"""Generate an AI-powered commit message"""
|
|
206
|
+
try:
|
|
207
|
+
# Analyze the changes first
|
|
208
|
+
analysis = self._analyze_file_patterns(changes)
|
|
209
|
+
|
|
210
|
+
# Create the prompt
|
|
211
|
+
prompt = self._create_commit_prompt(changes, diff_content, analysis)
|
|
212
|
+
|
|
213
|
+
# Generate using Ollama
|
|
214
|
+
response = ollama.generate(
|
|
215
|
+
model=self.model_name,
|
|
216
|
+
prompt=prompt,
|
|
217
|
+
options={
|
|
218
|
+
"temperature": self.temperature,
|
|
219
|
+
"top_p": 0.9,
|
|
220
|
+
"max_tokens": 200, # Keep commit messages concise
|
|
221
|
+
},
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
commit_message = response.get("response", "").strip()
|
|
225
|
+
|
|
226
|
+
# Clean up the response
|
|
227
|
+
commit_message = self._clean_commit_message(commit_message)
|
|
228
|
+
|
|
229
|
+
# Validate and provide fallback
|
|
230
|
+
if not commit_message or len(commit_message.split("\n")[0]) > 100:
|
|
231
|
+
logger.warning("AI generated invalid commit message, using fallback")
|
|
232
|
+
return self._generate_fallback_message(changes, analysis)
|
|
233
|
+
|
|
234
|
+
logger.info(f"Generated AI commit message: {commit_message}")
|
|
235
|
+
return commit_message
|
|
236
|
+
|
|
237
|
+
except Exception as e:
|
|
238
|
+
logger.error(f"AI commit message generation failed: {e}")
|
|
239
|
+
# Fallback to rule-based generation
|
|
240
|
+
return self._generate_fallback_message(
|
|
241
|
+
changes,
|
|
242
|
+
analysis if "analysis" in locals() else self._analyze_file_patterns(changes),
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
def _clean_commit_message(self, message: str) -> str:
|
|
246
|
+
"""Clean up AI generated commit message"""
|
|
247
|
+
lines = message.strip().split("\n")
|
|
248
|
+
|
|
249
|
+
# Remove any introductory text
|
|
250
|
+
cleaned_lines = []
|
|
251
|
+
for line in lines:
|
|
252
|
+
line = line.strip()
|
|
253
|
+
if not line:
|
|
254
|
+
continue
|
|
255
|
+
if line.lower().startswith(("here", "commit message:", "the commit", "generated")):
|
|
256
|
+
continue
|
|
257
|
+
if line.startswith('"') and line.endswith('"'):
|
|
258
|
+
line = line[1:-1]
|
|
259
|
+
if line.startswith("'") and line.endswith("'"):
|
|
260
|
+
line = line[1:-1]
|
|
261
|
+
cleaned_lines.append(line)
|
|
262
|
+
|
|
263
|
+
return "\n".join(cleaned_lines) if cleaned_lines else ""
|
|
264
|
+
|
|
265
|
+
def _generate_fallback_message(self, changes: Dict[str, Any], analysis: Dict[str, Any]) -> str:
|
|
266
|
+
"""Generate fallback commit message using rules"""
|
|
267
|
+
summary_parts = []
|
|
268
|
+
|
|
269
|
+
# Determine commit type based on analysis
|
|
270
|
+
if "tests" in analysis["categories"]:
|
|
271
|
+
commit_type = "test"
|
|
272
|
+
elif "docs" in analysis["categories"]:
|
|
273
|
+
commit_type = "docs"
|
|
274
|
+
elif "config" in analysis["categories"]:
|
|
275
|
+
commit_type = "chore"
|
|
276
|
+
elif "build" in analysis["categories"]:
|
|
277
|
+
commit_type = "build"
|
|
278
|
+
elif changes["changes"]["added"] and not changes["changes"]["modified"]:
|
|
279
|
+
commit_type = "feat"
|
|
280
|
+
elif changes["changes"]["deleted"] and not changes["changes"]["added"]:
|
|
281
|
+
commit_type = "refactor"
|
|
282
|
+
else:
|
|
283
|
+
commit_type = "chore"
|
|
284
|
+
|
|
285
|
+
# Create scope if languages are focused
|
|
286
|
+
scope = ""
|
|
287
|
+
if len(analysis["languages"]) == 1:
|
|
288
|
+
scope = f"({analysis['languages'][0]})"
|
|
289
|
+
elif len(analysis["categories"]) == 1 and analysis["categories"][0] not in [
|
|
290
|
+
"general",
|
|
291
|
+
"config",
|
|
292
|
+
]:
|
|
293
|
+
scope = f"({analysis['categories'][0]})"
|
|
294
|
+
|
|
295
|
+
# Generate description
|
|
296
|
+
if changes["changes"]["added"] and len(changes["changes"]["added"]) == 1:
|
|
297
|
+
description = f"add {changes['changes']['added'][0].split('/')[-1]}"
|
|
298
|
+
elif changes["changes"]["modified"] and len(changes["changes"]["modified"]) == 1:
|
|
299
|
+
description = f"update {changes['changes']['modified'][0].split('/')[-1]}"
|
|
300
|
+
elif changes["changes"]["deleted"] and len(changes["changes"]["deleted"]) == 1:
|
|
301
|
+
description = f"remove {changes['changes']['deleted'][0].split('/')[-1]}"
|
|
302
|
+
else:
|
|
303
|
+
total = changes["total_files"]
|
|
304
|
+
description = f"update {total} files"
|
|
305
|
+
|
|
306
|
+
return f"{commit_type}{scope}: {description}"
|
|
307
|
+
|
|
308
|
+
def test_ai_service(self) -> bool:
|
|
309
|
+
"""Test if the AI service is working properly"""
|
|
310
|
+
try:
|
|
311
|
+
# Test with minimal changes
|
|
312
|
+
test_changes = {
|
|
313
|
+
"total_files": 1,
|
|
314
|
+
"changes": {"added": ["test.py"], "modified": [], "deleted": [], "renamed": []},
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
test_diff = """
|
|
318
|
+
+def hello_world():
|
|
319
|
+
+ print("Hello, World!")
|
|
320
|
+
"""
|
|
321
|
+
|
|
322
|
+
message = self.generate_commit_message(test_changes, test_diff)
|
|
323
|
+
logger.info(f"AI service test successful: {message}")
|
|
324
|
+
return True
|
|
325
|
+
|
|
326
|
+
except Exception as e:
|
|
327
|
+
logger.error(f"AI service test failed: {e}")
|
|
328
|
+
return False
|