-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathai_service.py
More file actions
114 lines (98 loc) · 4.76 KB
/
ai_service.py
File metadata and controls
114 lines (98 loc) · 4.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
from openai import OpenAI
from config import ConfigManager
from exceptions import APIKeyError, AIServiceError
from token_management import TokenManager, FilePrioritizer
from diff_processor import DiffProcessor
class AIService:
def __init__(self):
self.config = ConfigManager()
self.client = None
self._init_client()
def _init_client(self):
"""Initializes the OpenAI client based on current config."""
api_key = self.config.api_key
base_url = self.config.get_api_base_url()
# Ollama local usually doesn't need a key, but OpenAI client requires one.
if self.config.get_provider() == "ollama" and not api_key:
api_key = "ollama"
if api_key:
self.client = OpenAI(
base_url=base_url,
api_key=api_key,
)
else:
self.client = None
def reload_config(self):
"""Reloads configuration and re-initializes the client."""
# Force a reload of the config manager's internal state
self.config = ConfigManager()
self._init_client()
def summarize_file_diff(self, file_diff_text):
if not self.client:
return "Summary unavailable (No AI Client)"
prompt = (
"Summarize the following git diff for a single file in 1-2 sentences. "
"Focus on WHAT changed (e.g. 'Updated dependency X', 'Refactored auth logic')."
)
try:
response = self.client.chat.completions.create(
model=self.config.model_name,
messages=[
{"role": "system", "content": prompt},
{"role": "user", "content": file_diff_text[:8000]} # Cap input for safety
]
)
return response.choices[0].message.content.strip()
except Exception:
return "Summary generation failed."
def generate_commit_message(self, diff_text):
if not self.client:
# If provider is ollama, client should have been init with "ollama" key
raise APIKeyError("API Key not configured. Please check your settings.")
if not diff_text or not diff_text.strip():
return "", "", False
# Intelligent Truncation
# Use a limit of 4000 tokens for the diff context.
processor = DiffProcessor(TokenManager(), FilePrioritizer(), self.summarize_file_diff)
processed_diff, truncated = processor.process_diff(diff_text, token_limit=4000)
default_system_prompt = (
"You are a helpful assistant that generates professional git commit messages based on diffs. "
"Rule 1: Use the conventional commits format if applicable. "
"Rule 2: Keep the summary line under 50 characters. "
"Rule 3: You must separate the Title and Description with '|||SEP|||'. "
"Format: Title|||SEP|||Description. "
"Rule 4: Use a concise bulleted list for details in present tense."
)
system_prompt = self.config.get_system_prompt() or default_system_prompt
# Ensure separator instruction is present even if user provides custom prompt
if "|||SEP|||" not in system_prompt:
system_prompt += " IMPORTANT: You must separate the Title and Description with '|||SEP|||'."
try:
response = self.client.chat.completions.create(
model=self.config.model_name,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": f"Here is the git diff:\n\n{processed_diff}"}
]
)
content = response.choices[0].message.content.strip()
# Robust Parsing
if "|||SEP|||" in content:
parts = content.split("|||SEP|||")
title = parts[0].strip()
desc = parts[1].strip() if len(parts) > 1 else ""
else:
# Fallback implementation
lines = content.split('\n')
title = lines[0].strip()
desc_lines = lines[1:]
# Remove leading empty lines from description
while desc_lines and not desc_lines[0].strip():
desc_lines.pop(0)
desc = "\n".join(desc_lines).strip()
return title, desc, truncated
except Exception as e:
error_str = str(e).lower()
if "bx" in error_str or "401" in error_str or "unauthorized" in error_str or "api key" in error_str:
raise APIKeyError(f"Invalid or missing API Key. Server returned: {e}", original_error=e)
raise AIServiceError(f"AI Service Error: {e}", original_error=e)