ServiceChannel webhook processor, proposal approval cards, attachment auto-post, CollabSupport commands, and Docker deployment configuration. Co-authored-by: Cursor <cursoragent@cursor.com>
150 lines
No EOL
4.7 KiB
JavaScript
150 lines
No EOL
4.7 KiB
JavaScript
import axios from 'axios';
|
||
import { loadSecrets } from '../../config/secrets.js';
|
||
|
||
const secrets = loadSecrets();
|
||
import { logger } from '../../utils/logger.js';
|
||
|
||
|
||
/**
|
||
* Summarizes the initial ticket description when a WorkOrderCreated webhook arrives.
|
||
* This produces a short, clean, professional summary suitable for posting as the
|
||
* first message in a new ServChan Webex space.
|
||
*/
|
||
export async function summarizeTicketDescription(
|
||
rawDescription,
|
||
xaiApiKey,
|
||
options = {}
|
||
) {
|
||
const startTime = Date.now();
|
||
const {
|
||
model = secrets.xai.model,
|
||
maxTokens = 300,
|
||
includeOriginal = false,
|
||
} = options;
|
||
|
||
if (!rawDescription?.trim()) {
|
||
return { summary: 'No description provided.' };
|
||
}
|
||
|
||
const token = xaiApiKey || secrets.xai.token;
|
||
if (!token) {
|
||
return {
|
||
summary: `(xAI not configured) Original: ${rawDescription.substring(0, 400)}${rawDescription.length > 400 ? '...' : ''}`,
|
||
};
|
||
}
|
||
|
||
const systemPrompt = `
|
||
You are an expert at turning messy, form-generated ticket descriptions into clear, concise, professional summaries
|
||
that someone can read in 10–15 seconds and immediately understand the issue.
|
||
|
||
Rules:
|
||
- Focus on: WHO is affected, WHAT is broken, WHERE it happens, WHEN it started / how often, IMPACT / severity
|
||
- Remove repetition, form boilerplate, labels like "Field1:", "Please select:", etc.
|
||
- Use natural, professional language — no emojis unless very clearly needed
|
||
- Keep technical terms if they are meaningful
|
||
- Aim for 2–6 sentences max
|
||
- If the text is already short & clear, just lightly clean it up
|
||
`.trim();
|
||
|
||
const userPrompt = `
|
||
Summarize the following ticket description:
|
||
|
||
"""
|
||
${rawDescription.trim()}
|
||
"""
|
||
|
||
Provide only the clean summary — no extra commentary, no "Here's a summary:", just the readable text.
|
||
`.trim();
|
||
|
||
try {
|
||
const response = await axios.post(
|
||
'https://api.x.ai/v1/chat/completions',
|
||
{
|
||
model,
|
||
messages: [
|
||
{ role: 'system', content: systemPrompt },
|
||
{ role: 'user', content: userPrompt },
|
||
],
|
||
temperature: 0.3,
|
||
max_tokens: maxTokens,
|
||
top_p: 0.95,
|
||
},
|
||
{
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
Authorization: `Bearer ${token}`,
|
||
},
|
||
timeout: 15000,
|
||
}
|
||
);
|
||
|
||
const summary = response.data.choices?.[0]?.message?.content?.trim() || '';
|
||
|
||
logger('xai:summarizeTicketDescription', `Finished in ${Date.now() - startTime}ms`);
|
||
|
||
if (includeOriginal) {
|
||
return { summary, original: rawDescription.trim() };
|
||
}
|
||
return { summary };
|
||
} catch (err) {
|
||
logger('xai:summarizeTicketDescription', `Error: ${err.message}`, 'error');
|
||
return {
|
||
summary: `(Summary unavailable) Original: ${rawDescription.substring(0, 400)}${rawDescription.length > 400 ? '...' : ''}`,
|
||
};
|
||
}
|
||
}
|
||
|
||
|
||
export async function summarizeTicketWithGrokFromContext(context, ticketId) {
|
||
if (!secrets.xai.token) {
|
||
return `(xAI not configured) Raw ticket info: ${context.substring(0, 200)}...`;
|
||
}
|
||
|
||
const systemPrompt = `
|
||
You are an expert HVAC/facilities technician and ServiceChannel ticket analyst.
|
||
|
||
Summarize this ticket clearly and concisely.
|
||
Use the **ticket description** as the primary source for the **main problem / reason for the ticket**.
|
||
Use the notes to provide timeline, actions, status updates, and pending items.
|
||
|
||
Structure your summary with these sections:
|
||
- **Main Problem** (from description)
|
||
- **Key Events & Timeline** (chronological bullets from notes, most recent last)
|
||
- **Actions Taken**
|
||
- **Current Status / Blockers**
|
||
- **Pending / Next Steps**
|
||
|
||
Keep it professional, neutral, factual, under 250 words.
|
||
Use bullet points where helpful.
|
||
If notes are repetitive, deduplicate them.
|
||
If no notes, omit "Key Events & Timeline" or say "No notes recorded."
|
||
`;
|
||
|
||
const userPrompt = `Summarize this ServiceChannel ticket:\n${context}`;
|
||
|
||
try {
|
||
const response = await axios.post(
|
||
'https://api.x.ai/v1/chat/completions',
|
||
{
|
||
model: secrets.xai.model,
|
||
messages: [
|
||
{ role: 'system', content: systemPrompt },
|
||
{ role: 'user', content: userPrompt }
|
||
],
|
||
temperature: 0.3,
|
||
max_tokens: 500
|
||
},
|
||
{
|
||
headers: {
|
||
Authorization: `Bearer ${secrets.xai.token}`,
|
||
'Content-Type': 'application/json'
|
||
}
|
||
}
|
||
);
|
||
|
||
return response.data.choices[0].message.content.trim();
|
||
} catch (err) {
|
||
console.error('[GROK] Error:', err.response?.data || err.message);
|
||
return `(Summary failed) Raw ticket info: ${context.substring(0, 200)}...`;
|
||
}
|
||
} |