servchan/src/integrations/xai/client.js
jmcqueen c2e98d105e Initial commit: ServChan Webex bot for ServiceChannel work orders.
ServiceChannel webhook processor, proposal approval cards, attachment auto-post,
CollabSupport commands, and Docker deployment configuration.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-24 15:12:08 -04:00

150 lines
No EOL
4.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 1015 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 26 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)}...`;
}
}