floating-copilot-widget 1.4.4 → 1.5.1
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.
- package/README.md +472 -16
- package/dist/index.d.ts +2 -0
- package/dist/index.esm.js +809 -1
- package/dist/index.js +829 -0
- package/dist/services/copilot.d.ts +91 -0
- package/dist/services/copilot.js +503 -0
- package/dist/services/integrations.d.ts +62 -0
- package/dist/services/integrations.js +281 -0
- package/dist/types/copilot.d.ts +207 -0
- package/dist/types/copilot.js +7 -0
- package/dist/types.d.ts +7 -0
- package/package.json +1 -1
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Copilot Integration Service
|
|
3
|
+
*
|
|
4
|
+
* Provides AI-powered intent analysis and API integration for the floating chat widget.
|
|
5
|
+
* Uses GitHub's Models API (backed by OpenAI GPT-4) for intelligent message understanding.
|
|
6
|
+
*
|
|
7
|
+
* To use this service, you need a GitHub Personal Access Token with appropriate scopes.
|
|
8
|
+
*/
|
|
9
|
+
import type { CopilotConfig, CopilotIntentRequest, CopilotIntentResponse, APIIntegration, APICallRequest, APICallResponse } from '../types/copilot';
|
|
10
|
+
export * from '../types/copilot';
|
|
11
|
+
/**
|
|
12
|
+
* Analyze user message to identify intent and extract parameters
|
|
13
|
+
*/
|
|
14
|
+
export declare function analyzeUserIntent(request: CopilotIntentRequest, config: CopilotConfig, availableApis?: APIIntegration[]): Promise<CopilotIntentResponse>;
|
|
15
|
+
/**
|
|
16
|
+
* Call an API integration based on intent and parameters
|
|
17
|
+
*/
|
|
18
|
+
export declare function callAPI(request: APICallRequest, integration: APIIntegration): Promise<APICallResponse>;
|
|
19
|
+
/**
|
|
20
|
+
* Process user message: analyze intent and call appropriate API
|
|
21
|
+
*/
|
|
22
|
+
export declare function processUserMessage(userMessage: string, config: CopilotConfig, availableApis: APIIntegration[], context?: Record<string, any>): Promise<{
|
|
23
|
+
intent: CopilotIntentResponse;
|
|
24
|
+
apiResponse?: APICallResponse;
|
|
25
|
+
}>;
|
|
26
|
+
/**
|
|
27
|
+
* Validate that GitHub token is available before processing
|
|
28
|
+
* Useful for early validation in applications
|
|
29
|
+
*/
|
|
30
|
+
export declare function validateGithubToken(config: CopilotConfig): {
|
|
31
|
+
valid: boolean;
|
|
32
|
+
token?: string;
|
|
33
|
+
error?: string;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Generate a user-friendly response from intent and API data
|
|
37
|
+
*/
|
|
38
|
+
export declare function generateCopilotResponse(userMessage: string, intentResponse: CopilotIntentResponse, apiResponse?: APICallResponse, config?: CopilotConfig): Promise<string>;
|
|
39
|
+
/**
|
|
40
|
+
* Validate API integration configuration
|
|
41
|
+
*/
|
|
42
|
+
export declare function validateAPIIntegration(api: APIIntegration): {
|
|
43
|
+
valid: boolean;
|
|
44
|
+
errors: string[];
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Merge multiple API integrations from different sources
|
|
48
|
+
*/
|
|
49
|
+
export declare function mergeAPIIntegrations(...integrationSets: APIIntegration[][]): APIIntegration[];
|
|
50
|
+
/**
|
|
51
|
+
* Get missing required parameters for an API
|
|
52
|
+
*/
|
|
53
|
+
export declare function getMissingParameters(api: APIIntegration, providedParams: Record<string, any>): string[];
|
|
54
|
+
/**
|
|
55
|
+
* Get the next missing parameter to ask for
|
|
56
|
+
*/
|
|
57
|
+
export declare function getNextMissingParameter(api: APIIntegration, providedParams: Record<string, any>): {
|
|
58
|
+
parameter: string;
|
|
59
|
+
description: string;
|
|
60
|
+
example?: string;
|
|
61
|
+
type?: string;
|
|
62
|
+
} | null;
|
|
63
|
+
/**
|
|
64
|
+
* Validate a parameter value against its definition
|
|
65
|
+
*/
|
|
66
|
+
export declare function validateParameter(paramName: string, value: any, definition: {
|
|
67
|
+
type?: string;
|
|
68
|
+
validation?: string;
|
|
69
|
+
}): {
|
|
70
|
+
valid: boolean;
|
|
71
|
+
error?: string;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Check if all required parameters have been collected
|
|
75
|
+
*/
|
|
76
|
+
export declare function areAllParametersCollected(api: APIIntegration, providedParams: Record<string, any>): boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Collect parameters interactively
|
|
79
|
+
* Returns a system prompt asking for the next missing parameter
|
|
80
|
+
* OR triggers API call if all parameters are collected
|
|
81
|
+
*/
|
|
82
|
+
export declare function generateParameterPromptOrCallAPI(api: APIIntegration, providedParams: Record<string, any>, intent: string, context?: Record<string, any>): Promise<{
|
|
83
|
+
prompt?: string;
|
|
84
|
+
apiResponse?: APICallResponse;
|
|
85
|
+
allParametersCollected: boolean;
|
|
86
|
+
}>;
|
|
87
|
+
/**
|
|
88
|
+
* Collect parameters interactively
|
|
89
|
+
* Returns a system prompt asking for the next missing parameter
|
|
90
|
+
*/
|
|
91
|
+
export declare function generateParameterPrompt(api: APIIntegration, providedParams: Record<string, any>): string;
|
|
@@ -0,0 +1,503 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Copilot Integration Service
|
|
3
|
+
*
|
|
4
|
+
* Provides AI-powered intent analysis and API integration for the floating chat widget.
|
|
5
|
+
* Uses GitHub's Models API (backed by OpenAI GPT-4) for intelligent message understanding.
|
|
6
|
+
*
|
|
7
|
+
* To use this service, you need a GitHub Personal Access Token with appropriate scopes.
|
|
8
|
+
*/
|
|
9
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
10
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
11
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
12
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
13
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
14
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
15
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
export * from '../types/copilot';
|
|
19
|
+
const GITHUB_COPILOT_MODELS = 'https://models.inference.ai.azure.com/chat/completions';
|
|
20
|
+
/**
|
|
21
|
+
* Get GitHub token from config or environment variables
|
|
22
|
+
*/
|
|
23
|
+
function getGithubToken(configToken) {
|
|
24
|
+
var _a, _b, _c;
|
|
25
|
+
// 1. Use explicitly provided token from config
|
|
26
|
+
if (configToken) {
|
|
27
|
+
return configToken;
|
|
28
|
+
}
|
|
29
|
+
// 2. Check common environment variable names
|
|
30
|
+
const token = (typeof process !== 'undefined' && ((_a = process.env) === null || _a === void 0 ? void 0 : _a.GITHUB_TOKEN)) ||
|
|
31
|
+
(typeof process !== 'undefined' && ((_b = process.env) === null || _b === void 0 ? void 0 : _b.VITE_GITHUB_TOKEN)) ||
|
|
32
|
+
(typeof process !== 'undefined' && ((_c = process.env) === null || _c === void 0 ? void 0 : _c.REACT_APP_GITHUB_TOKEN)) ||
|
|
33
|
+
(typeof window !== 'undefined' && window.__GITHUB_TOKEN__);
|
|
34
|
+
if (!token) {
|
|
35
|
+
throw new Error('GitHub token is required for Copilot integration. ' +
|
|
36
|
+
'Provide it via:\n' +
|
|
37
|
+
'1. CopilotConfig.githubToken parameter\n' +
|
|
38
|
+
'2. Environment variable: GITHUB_TOKEN, VITE_GITHUB_TOKEN, or REACT_APP_GITHUB_TOKEN\n' +
|
|
39
|
+
'3. Global variable: window.__GITHUB_TOKEN__\n\n' +
|
|
40
|
+
'Get token from: https://github.com/settings/tokens (scopes: copilot, read:user)');
|
|
41
|
+
}
|
|
42
|
+
return token;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Analyze user message to identify intent and extract parameters
|
|
46
|
+
*/
|
|
47
|
+
export function analyzeUserIntent(request, config, availableApis) {
|
|
48
|
+
var _a, _b, _c;
|
|
49
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
50
|
+
const { userMessage, context, customInstructions } = request;
|
|
51
|
+
const { model = 'gpt-4o', maxTokens = 2000 } = config;
|
|
52
|
+
const githubToken = getGithubToken(config.githubToken);
|
|
53
|
+
// Build system prompt
|
|
54
|
+
const systemPrompt = buildIntentSystemPrompt(availableApis, customInstructions);
|
|
55
|
+
// Build user prompt
|
|
56
|
+
const userPrompt = buildIntentUserPrompt(userMessage, context, availableApis);
|
|
57
|
+
try {
|
|
58
|
+
const response = yield fetch(GITHUB_COPILOT_MODELS, {
|
|
59
|
+
method: 'POST',
|
|
60
|
+
headers: {
|
|
61
|
+
'Authorization': `Bearer ${githubToken}`,
|
|
62
|
+
'Content-Type': 'application/json',
|
|
63
|
+
'X-GitHub-Api-Version': '2022-11-28'
|
|
64
|
+
},
|
|
65
|
+
body: JSON.stringify({
|
|
66
|
+
model: model,
|
|
67
|
+
messages: [
|
|
68
|
+
{ role: 'system', content: systemPrompt },
|
|
69
|
+
{ role: 'user', content: userPrompt }
|
|
70
|
+
],
|
|
71
|
+
max_tokens: maxTokens,
|
|
72
|
+
temperature: 0.3
|
|
73
|
+
})
|
|
74
|
+
});
|
|
75
|
+
if (!response.ok) {
|
|
76
|
+
const errorText = yield response.text();
|
|
77
|
+
throw new Error(`Copilot API error (${response.status}): ${errorText}`);
|
|
78
|
+
}
|
|
79
|
+
const data = yield response.json();
|
|
80
|
+
const content = (_c = (_b = (_a = data.choices) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.message) === null || _c === void 0 ? void 0 : _c.content;
|
|
81
|
+
if (!content) {
|
|
82
|
+
throw new Error('No response received from Copilot');
|
|
83
|
+
}
|
|
84
|
+
// Parse the intent response
|
|
85
|
+
return parseIntentResponse(content, userMessage);
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
throw new Error(`Failed to analyze intent: ${error.message}`);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Call an API integration based on intent and parameters
|
|
94
|
+
*/
|
|
95
|
+
export function callAPI(request, integration) {
|
|
96
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
97
|
+
try {
|
|
98
|
+
// Build the request
|
|
99
|
+
const apiRequest = integration.buildRequest(request.parameters, request.context);
|
|
100
|
+
// Prepare fetch options
|
|
101
|
+
const fetchOptions = {
|
|
102
|
+
method: integration.method,
|
|
103
|
+
headers: Object.assign({ 'Content-Type': 'application/json' }, integration.headers)
|
|
104
|
+
};
|
|
105
|
+
// Add body for non-GET requests
|
|
106
|
+
if (integration.method !== 'GET' && apiRequest) {
|
|
107
|
+
fetchOptions.body = typeof apiRequest === 'string' ? apiRequest : JSON.stringify(apiRequest);
|
|
108
|
+
}
|
|
109
|
+
// Make the API call
|
|
110
|
+
const response = yield fetch(integration.endpoint, fetchOptions);
|
|
111
|
+
if (!response.ok) {
|
|
112
|
+
const errorText = yield response.text();
|
|
113
|
+
return {
|
|
114
|
+
success: false,
|
|
115
|
+
error: `API error (${response.status}): ${errorText}`,
|
|
116
|
+
apiName: integration.name
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
const responseData = yield response.json();
|
|
120
|
+
// Format the response
|
|
121
|
+
const formattedData = integration.formatResponse(responseData);
|
|
122
|
+
return {
|
|
123
|
+
success: true,
|
|
124
|
+
data: formattedData,
|
|
125
|
+
rawData: responseData,
|
|
126
|
+
apiName: integration.name
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
return {
|
|
131
|
+
success: false,
|
|
132
|
+
error: `Failed to call API: ${error.message}`,
|
|
133
|
+
apiName: integration.name
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Process user message: analyze intent and call appropriate API
|
|
140
|
+
*/
|
|
141
|
+
export function processUserMessage(userMessage, config, availableApis, context) {
|
|
142
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
143
|
+
// Validate token is available early
|
|
144
|
+
getGithubToken(config.githubToken);
|
|
145
|
+
// Step 1: Analyze intent
|
|
146
|
+
const intentResponse = yield analyzeUserIntent({ userMessage, context }, config, availableApis);
|
|
147
|
+
// Step 2: Determine which API to call
|
|
148
|
+
let apiResponse;
|
|
149
|
+
if (intentResponse.suggestedApis.length > 0) {
|
|
150
|
+
const apiId = intentResponse.suggestedApis[0];
|
|
151
|
+
const api = availableApis.find(a => a.id === apiId);
|
|
152
|
+
if (api) {
|
|
153
|
+
// Check if API should be called for this intent
|
|
154
|
+
const shouldCall = api.shouldCall
|
|
155
|
+
? api.shouldCall(intentResponse.intent, intentResponse.parameters)
|
|
156
|
+
: true;
|
|
157
|
+
if (shouldCall) {
|
|
158
|
+
apiResponse = yield callAPI({
|
|
159
|
+
apiId,
|
|
160
|
+
parameters: intentResponse.parameters,
|
|
161
|
+
context
|
|
162
|
+
}, api);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
intent: intentResponse,
|
|
168
|
+
apiResponse
|
|
169
|
+
};
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Validate that GitHub token is available before processing
|
|
174
|
+
* Useful for early validation in applications
|
|
175
|
+
*/
|
|
176
|
+
export function validateGithubToken(config) {
|
|
177
|
+
try {
|
|
178
|
+
const token = getGithubToken(config.githubToken);
|
|
179
|
+
return { valid: true, token };
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
return { valid: false, error: error.message };
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Generate a user-friendly response from intent and API data
|
|
187
|
+
*/
|
|
188
|
+
export function generateCopilotResponse(userMessage, intentResponse, apiResponse, config) {
|
|
189
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
190
|
+
// If we have API response data, return it
|
|
191
|
+
if ((apiResponse === null || apiResponse === void 0 ? void 0 : apiResponse.success) && apiResponse.data) {
|
|
192
|
+
return apiResponse.data;
|
|
193
|
+
}
|
|
194
|
+
// If API call failed, explain the error
|
|
195
|
+
if (apiResponse === null || apiResponse === void 0 ? void 0 : apiResponse.error) {
|
|
196
|
+
return `I encountered an error while processing your request: ${apiResponse.error}`;
|
|
197
|
+
}
|
|
198
|
+
// If no API was called but we have intent analysis
|
|
199
|
+
if (intentResponse.intent && intentResponse.confidence > 0.5) {
|
|
200
|
+
return `I understand you want to: ${intentResponse.intent}
|
|
201
|
+
|
|
202
|
+
Parameters identified:
|
|
203
|
+
${Object.entries(intentResponse.parameters)
|
|
204
|
+
.map(([key, value]) => `- ${key}: ${JSON.stringify(value)}`)
|
|
205
|
+
.join('\n')}
|
|
206
|
+
|
|
207
|
+
However, no API integration is available for this intent. Please check the available integrations.`;
|
|
208
|
+
}
|
|
209
|
+
// Fallback response
|
|
210
|
+
return `I'm not sure what you're asking. Could you provide more details?`;
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Build system prompt for intent analysis
|
|
215
|
+
*/
|
|
216
|
+
function buildIntentSystemPrompt(availableApis, customInstructions) {
|
|
217
|
+
let prompt = `You are an intelligent intent analyzer integrated into a chat application.
|
|
218
|
+
Your role is to understand user messages and identify the user's intent, extract relevant parameters, and suggest which API integrations to use.
|
|
219
|
+
|
|
220
|
+
RESPONSE FORMAT:
|
|
221
|
+
You MUST respond ONLY with a valid JSON object in this exact format:
|
|
222
|
+
{
|
|
223
|
+
"intent": "description of what the user wants",
|
|
224
|
+
"confidence": 0.95,
|
|
225
|
+
"parameters": {
|
|
226
|
+
"param1": "value1",
|
|
227
|
+
"param2": "value2"
|
|
228
|
+
},
|
|
229
|
+
"suggestedApis": ["api_id_1", "api_id_2"]
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
IMPORTANT:
|
|
233
|
+
- intent: A clear description of what the user wants to accomplish
|
|
234
|
+
- confidence: A number between 0 and 1 indicating how confident you are
|
|
235
|
+
- parameters: Key-value pairs of information extracted from the message
|
|
236
|
+
- suggestedApis: Array of API IDs that should be called (in order of preference)
|
|
237
|
+
|
|
238
|
+
Be conversational, helpful, and focus on understanding the user's actual need.`;
|
|
239
|
+
if (availableApis && availableApis.length > 0) {
|
|
240
|
+
prompt += `\n\nAVAILABLE API INTEGRATIONS:\n`;
|
|
241
|
+
availableApis.forEach(api => {
|
|
242
|
+
prompt += `\n- ID: ${api.id}\n Name: ${api.name}\n Description: ${api.name}`;
|
|
243
|
+
if (api.intents) {
|
|
244
|
+
prompt += `\n Intended for intents: ${api.intents.join(', ')}`;
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
if (customInstructions) {
|
|
249
|
+
prompt += `\n\nCUSTOM INSTRUCTIONS:\n${customInstructions}`;
|
|
250
|
+
}
|
|
251
|
+
return prompt;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Build user prompt for intent analysis
|
|
255
|
+
*/
|
|
256
|
+
function buildIntentUserPrompt(userMessage, context, availableApis) {
|
|
257
|
+
let prompt = `Analyze this user message and respond with ONLY a valid JSON object (no markdown, no extra text):
|
|
258
|
+
|
|
259
|
+
User message: "${userMessage}"`;
|
|
260
|
+
if (context && Object.keys(context).length > 0) {
|
|
261
|
+
prompt += `\n\nContext information:\n`;
|
|
262
|
+
Object.entries(context).forEach(([key, value]) => {
|
|
263
|
+
prompt += `- ${key}: ${JSON.stringify(value)}\n`;
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
prompt += `\n\nRespond with ONLY the JSON object, no other text.`;
|
|
267
|
+
return prompt;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Parse intent response from Copilot
|
|
271
|
+
*/
|
|
272
|
+
function parseIntentResponse(content, userMessage) {
|
|
273
|
+
try {
|
|
274
|
+
// Extract JSON from potential markdown code blocks
|
|
275
|
+
let jsonContent = content.trim();
|
|
276
|
+
const jsonMatch = content.match(/```(?:json)?\s*([\s\S]*?)\s*```/);
|
|
277
|
+
if (jsonMatch) {
|
|
278
|
+
jsonContent = jsonMatch[1];
|
|
279
|
+
}
|
|
280
|
+
const parsed = JSON.parse(jsonContent);
|
|
281
|
+
return {
|
|
282
|
+
intent: parsed.intent || 'unknown',
|
|
283
|
+
confidence: typeof parsed.confidence === 'number' ? parsed.confidence : 0.5,
|
|
284
|
+
parameters: parsed.parameters || {},
|
|
285
|
+
suggestedApis: Array.isArray(parsed.suggestedApis) ? parsed.suggestedApis : [],
|
|
286
|
+
rawResponse: content
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
catch (error) {
|
|
290
|
+
// Fallback parsing if JSON parsing fails
|
|
291
|
+
return {
|
|
292
|
+
intent: 'user_query',
|
|
293
|
+
confidence: 0.3,
|
|
294
|
+
parameters: { query: userMessage },
|
|
295
|
+
suggestedApis: [],
|
|
296
|
+
rawResponse: content
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Validate API integration configuration
|
|
302
|
+
*/
|
|
303
|
+
export function validateAPIIntegration(api) {
|
|
304
|
+
const errors = [];
|
|
305
|
+
if (!api.id)
|
|
306
|
+
errors.push('API must have an id');
|
|
307
|
+
if (!api.name)
|
|
308
|
+
errors.push('API must have a name');
|
|
309
|
+
if (!api.endpoint)
|
|
310
|
+
errors.push('API must have an endpoint');
|
|
311
|
+
if (!api.method)
|
|
312
|
+
errors.push('API must have a method');
|
|
313
|
+
if (!api.buildRequest || typeof api.buildRequest !== 'function') {
|
|
314
|
+
errors.push('API must have a buildRequest function');
|
|
315
|
+
}
|
|
316
|
+
if (!api.formatResponse || typeof api.formatResponse !== 'function') {
|
|
317
|
+
errors.push('API must have a formatResponse function');
|
|
318
|
+
}
|
|
319
|
+
return {
|
|
320
|
+
valid: errors.length === 0,
|
|
321
|
+
errors
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Merge multiple API integrations from different sources
|
|
326
|
+
*/
|
|
327
|
+
export function mergeAPIIntegrations(...integrationSets) {
|
|
328
|
+
const merged = new Map();
|
|
329
|
+
integrationSets.forEach(set => {
|
|
330
|
+
set.forEach(api => {
|
|
331
|
+
const validation = validateAPIIntegration(api);
|
|
332
|
+
if (validation.valid) {
|
|
333
|
+
merged.set(api.id, api);
|
|
334
|
+
}
|
|
335
|
+
else {
|
|
336
|
+
console.warn(`Invalid API integration ${api.id}:`, validation.errors);
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
return Array.from(merged.values());
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Get missing required parameters for an API
|
|
344
|
+
*/
|
|
345
|
+
export function getMissingParameters(api, providedParams) {
|
|
346
|
+
if (!api.requiredParameters || api.requiredParameters.length === 0) {
|
|
347
|
+
return [];
|
|
348
|
+
}
|
|
349
|
+
return api.requiredParameters
|
|
350
|
+
.filter(param => param.required !== false && !providedParams[param.name])
|
|
351
|
+
.map(param => param.name);
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Get the next missing parameter to ask for
|
|
355
|
+
*/
|
|
356
|
+
export function getNextMissingParameter(api, providedParams) {
|
|
357
|
+
if (!api.requiredParameters || api.requiredParameters.length === 0) {
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
const missing = api.requiredParameters.find(param => param.required !== false && !providedParams[param.name]);
|
|
361
|
+
if (!missing) {
|
|
362
|
+
return null;
|
|
363
|
+
}
|
|
364
|
+
return {
|
|
365
|
+
parameter: missing.name,
|
|
366
|
+
description: missing.description,
|
|
367
|
+
example: missing.example,
|
|
368
|
+
type: missing.type
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Validate a parameter value against its definition
|
|
373
|
+
*/
|
|
374
|
+
export function validateParameter(paramName, value, definition) {
|
|
375
|
+
if (!value) {
|
|
376
|
+
return { valid: false, error: `${paramName} cannot be empty` };
|
|
377
|
+
}
|
|
378
|
+
// Type validation
|
|
379
|
+
if (definition.type) {
|
|
380
|
+
switch (definition.type) {
|
|
381
|
+
case 'email':
|
|
382
|
+
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
|
|
383
|
+
return { valid: false, error: `${paramName} must be a valid email address` };
|
|
384
|
+
}
|
|
385
|
+
break;
|
|
386
|
+
case 'phone':
|
|
387
|
+
if (!/^[\d\s\-+()]+$/.test(value) || value.replace(/\D/g, '').length < 10) {
|
|
388
|
+
return { valid: false, error: `${paramName} must be a valid phone number` };
|
|
389
|
+
}
|
|
390
|
+
break;
|
|
391
|
+
case 'number':
|
|
392
|
+
if (isNaN(Number(value))) {
|
|
393
|
+
return { valid: false, error: `${paramName} must be a number` };
|
|
394
|
+
}
|
|
395
|
+
break;
|
|
396
|
+
case 'date':
|
|
397
|
+
if (isNaN(Date.parse(value))) {
|
|
398
|
+
return { valid: false, error: `${paramName} must be a valid date` };
|
|
399
|
+
}
|
|
400
|
+
break;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
// Regex validation
|
|
404
|
+
if (definition.validation) {
|
|
405
|
+
const regex = new RegExp(definition.validation);
|
|
406
|
+
if (!regex.test(value)) {
|
|
407
|
+
return { valid: false, error: `${paramName} format is invalid` };
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
return { valid: true };
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Check if all required parameters have been collected
|
|
414
|
+
*/
|
|
415
|
+
export function areAllParametersCollected(api, providedParams) {
|
|
416
|
+
if (!api.requiredParameters || api.requiredParameters.length === 0) {
|
|
417
|
+
return true;
|
|
418
|
+
}
|
|
419
|
+
return !api.requiredParameters.some(param => param.required !== false && !providedParams[param.name]);
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Collect parameters interactively
|
|
423
|
+
* Returns a system prompt asking for the next missing parameter
|
|
424
|
+
* OR triggers API call if all parameters are collected
|
|
425
|
+
*/
|
|
426
|
+
export function generateParameterPromptOrCallAPI(api, providedParams, intent, context) {
|
|
427
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
428
|
+
// Check if all required parameters are collected
|
|
429
|
+
const allCollected = areAllParametersCollected(api, providedParams);
|
|
430
|
+
if (allCollected) {
|
|
431
|
+
// All parameters are collected - call the API
|
|
432
|
+
try {
|
|
433
|
+
const apiResponse = yield callAPI({
|
|
434
|
+
apiId: api.id,
|
|
435
|
+
parameters: providedParams,
|
|
436
|
+
context
|
|
437
|
+
}, api);
|
|
438
|
+
return {
|
|
439
|
+
apiResponse,
|
|
440
|
+
allParametersCollected: true
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
catch (error) {
|
|
444
|
+
return {
|
|
445
|
+
prompt: `Error executing request: ${error.message}`,
|
|
446
|
+
allParametersCollected: true
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
// Still missing parameters - ask for the next one
|
|
451
|
+
const nextParam = getNextMissingParameter(api, providedParams);
|
|
452
|
+
if (!nextParam) {
|
|
453
|
+
return {
|
|
454
|
+
allParametersCollected: true
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
const provided = Object.keys(providedParams)
|
|
458
|
+
.filter(key => providedParams[key])
|
|
459
|
+
.map(key => `- ${key}: ${providedParams[key]}`)
|
|
460
|
+
.join('\n');
|
|
461
|
+
let prompt = `To complete your request to ${intent}, I need the following information:\n\n`;
|
|
462
|
+
prompt += `**${nextParam.parameter}**: ${nextParam.description}`;
|
|
463
|
+
if (nextParam.type) {
|
|
464
|
+
prompt += ` (Type: ${nextParam.type})`;
|
|
465
|
+
}
|
|
466
|
+
if (nextParam.example) {
|
|
467
|
+
prompt += `\nExample: ${nextParam.example}`;
|
|
468
|
+
}
|
|
469
|
+
if (provided) {
|
|
470
|
+
prompt += `\n\nAlready provided:\n${provided}`;
|
|
471
|
+
}
|
|
472
|
+
return {
|
|
473
|
+
prompt,
|
|
474
|
+
allParametersCollected: false
|
|
475
|
+
};
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Collect parameters interactively
|
|
480
|
+
* Returns a system prompt asking for the next missing parameter
|
|
481
|
+
*/
|
|
482
|
+
export function generateParameterPrompt(api, providedParams) {
|
|
483
|
+
const nextParam = getNextMissingParameter(api, providedParams);
|
|
484
|
+
if (!nextParam) {
|
|
485
|
+
return '';
|
|
486
|
+
}
|
|
487
|
+
const provided = Object.keys(providedParams)
|
|
488
|
+
.filter(key => providedParams[key])
|
|
489
|
+
.map(key => `- ${key}: ${providedParams[key]}`)
|
|
490
|
+
.join('\n');
|
|
491
|
+
let prompt = `To complete your request, I need the following information:\n\n`;
|
|
492
|
+
prompt += `**${nextParam.parameter}**: ${nextParam.description}`;
|
|
493
|
+
if (nextParam.type) {
|
|
494
|
+
prompt += ` (Type: ${nextParam.type})`;
|
|
495
|
+
}
|
|
496
|
+
if (nextParam.example) {
|
|
497
|
+
prompt += `\nExample: ${nextParam.example}`;
|
|
498
|
+
}
|
|
499
|
+
if (provided) {
|
|
500
|
+
prompt += `\n\nAlready provided:\n${provided}`;
|
|
501
|
+
}
|
|
502
|
+
return prompt;
|
|
503
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Integration Examples
|
|
3
|
+
*
|
|
4
|
+
* This file provides examples of how to create and configure API integrations
|
|
5
|
+
* for use with the Copilot chat widget.
|
|
6
|
+
*/
|
|
7
|
+
import type { APIIntegration } from '../types/copilot';
|
|
8
|
+
/**
|
|
9
|
+
* Example: Customer Creation API with Required Parameters
|
|
10
|
+
*
|
|
11
|
+
* This example shows how to integrate an API that creates a customer
|
|
12
|
+
* with interactive parameter collection
|
|
13
|
+
*/
|
|
14
|
+
export declare const createCustomerAPIIntegration: (baseUrl: string) => APIIntegration;
|
|
15
|
+
/**
|
|
16
|
+
* Example: REST API Integration
|
|
17
|
+
*
|
|
18
|
+
* This example shows how to integrate a generic REST API that returns user data
|
|
19
|
+
*/
|
|
20
|
+
export declare const createUserAPIIntegration: (baseUrl: string) => APIIntegration;
|
|
21
|
+
/**
|
|
22
|
+
* Example: Data Analysis API Integration
|
|
23
|
+
*
|
|
24
|
+
* This shows how to integrate an API that performs data analysis
|
|
25
|
+
*/
|
|
26
|
+
export declare const createAnalysisAPIIntegration: (baseUrl: string) => APIIntegration;
|
|
27
|
+
/**
|
|
28
|
+
* Example: Search API Integration
|
|
29
|
+
*
|
|
30
|
+
* Shows how to integrate a search/query API
|
|
31
|
+
*/
|
|
32
|
+
export declare const createSearchAPIIntegration: (baseUrl: string) => APIIntegration;
|
|
33
|
+
/**
|
|
34
|
+
* Example: Webhook/Action API Integration
|
|
35
|
+
*
|
|
36
|
+
* Shows how to integrate APIs that perform actions (create, update, delete)
|
|
37
|
+
*/
|
|
38
|
+
export declare const createActionAPIIntegration: (baseUrl: string) => APIIntegration;
|
|
39
|
+
/**
|
|
40
|
+
* Example: Database Query Integration
|
|
41
|
+
*
|
|
42
|
+
* Shows how to integrate a database query service
|
|
43
|
+
*/
|
|
44
|
+
export declare const createDatabaseAPIIntegration: (baseUrl: string) => APIIntegration;
|
|
45
|
+
/**
|
|
46
|
+
* Example: External Service Integration (Weather, News, etc.)
|
|
47
|
+
*/
|
|
48
|
+
export declare const createWeatherAPIIntegration: (apiKey: string) => APIIntegration;
|
|
49
|
+
/**
|
|
50
|
+
* Helper function to create a custom API integration
|
|
51
|
+
*/
|
|
52
|
+
export declare function createCustomAPIIntegration(config: {
|
|
53
|
+
id: string;
|
|
54
|
+
name: string;
|
|
55
|
+
endpoint: string;
|
|
56
|
+
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
57
|
+
intents?: string[];
|
|
58
|
+
headers?: Record<string, string>;
|
|
59
|
+
buildRequest: (params: Record<string, any>, context?: Record<string, any>) => any;
|
|
60
|
+
formatResponse: (data: any) => string;
|
|
61
|
+
shouldCall?: (intent: string, parameters: Record<string, any>) => boolean;
|
|
62
|
+
}): APIIntegration;
|