Update chatbot
This commit is contained in:
+66
-31
@@ -29,6 +29,53 @@ Your job is to:
|
||||
7. Always ask clarifying questions if you need more detail before creating a ticket.
|
||||
"""
|
||||
|
||||
# Groq API — OpenAI-compatible, free tier, no region restrictions.
|
||||
# Free tier: 14,400 requests/day. Get a key at: https://console.groq.com
|
||||
_GROQ_API_URL = 'https://api.groq.com/openai/v1/chat/completions'
|
||||
_GROQ_MODEL = 'llama-3.3-70b-versatile'
|
||||
|
||||
|
||||
def _call_groq(api_key, history, user_msg):
|
||||
"""
|
||||
Call the Groq API (OpenAI-compatible) and return the assistant's reply text.
|
||||
|
||||
The system prompt is prepended as a system message. Prior history and the
|
||||
new user message are appended in order.
|
||||
|
||||
Raises requests.HTTPError or requests.exceptions.RequestException on failure.
|
||||
"""
|
||||
model = current_app.config.get('GROQ_MODEL', _GROQ_MODEL)
|
||||
|
||||
# Strip any assistant messages containing a create_ticket action block.
|
||||
# These should never reach the model — if they do, the model re-triggers
|
||||
# ticket creation on every subsequent turn.
|
||||
clean_history = [
|
||||
msg for msg in history
|
||||
if not (msg.get('role') == 'assistant' and '"action": "create_ticket"' in msg.get('content', ''))
|
||||
]
|
||||
|
||||
messages = (
|
||||
[{'role': 'system', 'content': _SYSTEM_PROMPT}]
|
||||
+ clean_history
|
||||
+ [{'role': 'user', 'content': user_msg}]
|
||||
)
|
||||
resp = requests.post(
|
||||
_GROQ_API_URL,
|
||||
headers={
|
||||
'Authorization': f'Bearer {api_key}',
|
||||
'Content-Type' : 'application/json',
|
||||
},
|
||||
json={
|
||||
'model' : model,
|
||||
'messages' : messages,
|
||||
'max_tokens' : 1024,
|
||||
'temperature': 0.4,
|
||||
},
|
||||
timeout=30,
|
||||
)
|
||||
resp.raise_for_status()
|
||||
return resp.json()['choices'][0]['message']['content'].strip()
|
||||
|
||||
|
||||
@chatbot_bp.route('/message', methods=['POST'])
|
||||
@login_required
|
||||
@@ -40,40 +87,28 @@ def chat():
|
||||
if not user_msg:
|
||||
return jsonify({'error': 'Empty message'}), 400
|
||||
|
||||
api_key = current_app.config.get('ANTHROPIC_API_KEY', '')
|
||||
api_key = current_app.config.get('GROQ_API_KEY', '')
|
||||
if not api_key:
|
||||
return jsonify({'reply': "The AI assistant is not configured yet. Please contact your IT administrator.", 'ticket': None})
|
||||
|
||||
messages = history + [{'role': 'user', 'content': user_msg}]
|
||||
return jsonify({
|
||||
'reply' : 'The AI assistant is not configured yet. Please contact your IT administrator.',
|
||||
'ticket': None,
|
||||
})
|
||||
|
||||
try:
|
||||
resp = requests.post(
|
||||
'https://api.anthropic.com/v1/messages',
|
||||
headers={
|
||||
'x-api-key' : api_key,
|
||||
'anthropic-version': '2023-06-01',
|
||||
'content-type' : 'application/json',
|
||||
},
|
||||
json={
|
||||
'model' : 'claude-sonnet-4-20250514',
|
||||
'max_tokens': 1024,
|
||||
'system' : _SYSTEM_PROMPT,
|
||||
'messages' : messages,
|
||||
},
|
||||
timeout=30,
|
||||
)
|
||||
resp.raise_for_status()
|
||||
reply_text = resp.json()['content'][0]['text'].strip()
|
||||
reply_text = _call_groq(api_key, history, user_msg)
|
||||
except Exception as exc:
|
||||
logger.error(f'[CHATBOT API ERROR] {exc}')
|
||||
return jsonify({'reply': 'Sorry, I encountered an error. Please try again or submit a ticket manually.', 'ticket': None})
|
||||
return jsonify({
|
||||
'reply' : 'Sorry, I encountered an error. Please try again or submit a ticket manually.',
|
||||
'ticket': None,
|
||||
})
|
||||
|
||||
# Check if the AI wants to create a ticket
|
||||
ticket_data = None
|
||||
if '"action": "create_ticket"' in reply_text or "'action': 'create_ticket'" in reply_text:
|
||||
try:
|
||||
start = reply_text.find('{')
|
||||
end = reply_text.rfind('}') + 1
|
||||
start = reply_text.find('{')
|
||||
end = reply_text.rfind('}') + 1
|
||||
parsed = json.loads(reply_text[start:end])
|
||||
if parsed.get('action') == 'create_ticket':
|
||||
# Validate AI-provided enum values against allowed sets to
|
||||
@@ -89,8 +124,8 @@ def chat():
|
||||
TicketPriority.LOW, TicketPriority.MEDIUM,
|
||||
TicketPriority.HIGH, TicketPriority.CRITICAL,
|
||||
}
|
||||
raw_category = parsed.get('category', TicketCategory.OTHER)
|
||||
raw_priority = parsed.get('priority', TicketPriority.MEDIUM)
|
||||
raw_category = parsed.get('category', TicketCategory.OTHER)
|
||||
raw_priority = parsed.get('priority', TicketPriority.MEDIUM)
|
||||
safe_category = raw_category if raw_category in _valid_categories else TicketCategory.OTHER
|
||||
safe_priority = raw_priority if raw_priority in _valid_priorities else TicketPriority.MEDIUM
|
||||
if raw_category != safe_category:
|
||||
@@ -118,10 +153,10 @@ def chat():
|
||||
notify_new_ticket(ticket)
|
||||
|
||||
ticket_data = {
|
||||
'id' : ticket.id,
|
||||
'ticket_number' : ticket.ticket_number,
|
||||
'title' : ticket.title,
|
||||
'url' : f'/tickets/{ticket.id}',
|
||||
'id' : ticket.id,
|
||||
'ticket_number': ticket.ticket_number,
|
||||
'title' : ticket.title,
|
||||
'url' : f'/tickets/{ticket.id}',
|
||||
}
|
||||
reply_text = (
|
||||
f"✅ **Ticket Created!**\n\n"
|
||||
@@ -132,4 +167,4 @@ def chat():
|
||||
except (json.JSONDecodeError, KeyError) as exc:
|
||||
logger.warning(f'[CHATBOT PARSE ERROR] Could not parse ticket JSON: {exc}')
|
||||
|
||||
return jsonify({'reply': reply_text, 'ticket': ticket_data})
|
||||
return jsonify({'reply': reply_text, 'ticket': ticket_data})
|
||||
Reference in New Issue
Block a user