feat: Add comprehensive debugging tools for Bayer MGA integration

- Added debug guide with manual testing procedures
- Created debug API route for testing endpoints
- Includes models endpoint, chat completions, and AI SDK testing
- Provides detailed error logging and response analysis
- CORS support for browser testing
This commit is contained in:
Nirmal Arya 2025-05-31 13:53:09 -04:00
parent 2c9926fc9d
commit 671fc51fa5

View File

@ -0,0 +1,286 @@
import { type ActionFunctionArgs, type LoaderFunctionArgs, json } from '@remix-run/cloudflare';
import { getApiKeysFromCookie, getProviderSettingsFromCookie } from '~/lib/api/cookies';
import { createScopedLogger } from '~/utils/logger';
import { BaseProvider, getOpenAILikeModel } from '~/lib/modules/llm/base-provider';
import { generateText } from 'ai';
const logger = createScopedLogger('BayerMGADebug');
// Add CORS headers to all responses
function addCorsHeaders(response: Response): Response {
response.headers.set('Access-Control-Allow-Origin', '*');
response.headers.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
response.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
return response;
}
// Handle OPTIONS requests for CORS preflight
export async function loader({ request }: LoaderFunctionArgs) {
if (request.method === 'OPTIONS') {
return addCorsHeaders(new Response(null, { status: 204 }));
}
const url = new URL(request.url);
const apiKey = url.searchParams.get('apiKey');
const baseUrl = url.searchParams.get('baseUrl') || 'https://chat.int.bayer.com/api/v2';
const model = url.searchParams.get('model') || 'gpt-4o-mini';
// Get API key from cookies if not provided in query params
let effectiveApiKey = apiKey;
if (!effectiveApiKey) {
const cookieHeader = request.headers.get('Cookie');
const apiKeys = getApiKeysFromCookie(cookieHeader);
effectiveApiKey = apiKeys?.['BayerMGA'];
}
if (!effectiveApiKey) {
return addCorsHeaders(
json(
{
success: false,
error: 'No API key provided. Add ?apiKey=your_key to the URL or set it in the app settings.',
tests: [],
},
{ status: 400 },
),
);
}
// Normalize base URL (remove trailing slash)
const normalizedBaseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;
// Results container
const results = {
success: true,
baseUrl: normalizedBaseUrl,
model,
apiKeyProvided: !!effectiveApiKey,
apiKeyPrefix: effectiveApiKey ? `${effectiveApiKey.substring(0, 4)}...` : null,
tests: [] as any[],
};
// Test 1: Fetch models
try {
logger.info(`Testing models endpoint: ${normalizedBaseUrl}/models`);
const modelsUrl = `${normalizedBaseUrl}/models?include_hidden_models=false&include_aliases=true`;
const modelsStartTime = Date.now();
const modelsResponse = await fetch(modelsUrl, {
headers: {
Authorization: `Bearer ${effectiveApiKey}`,
},
});
const modelsEndTime = Date.now();
const modelsResponseText = await modelsResponse.text();
let modelsData;
let parsedModels = [];
try {
modelsData = JSON.parse(modelsResponseText);
if (modelsData.data && Array.isArray(modelsData.data)) {
parsedModels = modelsData.data
.filter((model: any) => model.model_status === 'available')
.map((model: any) => ({
model: model.model,
name: model.name,
context_window: model.context_window,
}));
}
} catch (e) {
// JSON parse error
}
results.tests.push({
name: 'Models Endpoint',
url: modelsUrl,
success: modelsResponse.ok,
status: modelsResponse.status,
statusText: modelsResponse.statusText,
duration: `${modelsEndTime - modelsStartTime}ms`,
headers: Object.fromEntries(modelsResponse.headers.entries()),
responsePreview: modelsResponseText.substring(0, 500) + (modelsResponseText.length > 500 ? '...' : ''),
parsedModelsCount: parsedModels.length,
availableModels: parsedModels.slice(0, 5), // First 5 models only
});
// If models test failed, don't attempt completions test
if (!modelsResponse.ok) {
results.success = false;
return addCorsHeaders(json(results));
}
// Test 2: Chat completions
logger.info(`Testing chat completions endpoint: ${normalizedBaseUrl}/chat/completions`);
const completionsUrl = `${normalizedBaseUrl}/chat/completions`;
const completionsStartTime = Date.now();
const completionsResponse = await fetch(completionsUrl, {
method: 'POST',
headers: {
'Authorization': `Bearer ${effectiveApiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: model,
messages: [
{
role: 'user',
content: 'Hello from Buildify debug test. Please respond with a very short greeting.',
},
],
temperature: 0.2,
stream: false,
}),
});
const completionsEndTime = Date.now();
const completionsResponseText = await completionsResponse.text();
let completionsData;
let completionContent = null;
try {
completionsData = JSON.parse(completionsResponseText);
if (completionsData.choices && completionsData.choices.length > 0) {
completionContent = completionsData.choices[0].message?.content;
}
} catch (e) {
// JSON parse error
}
results.tests.push({
name: 'Chat Completions Endpoint',
url: completionsUrl,
success: completionsResponse.ok,
status: completionsResponse.status,
statusText: completionsResponse.statusText,
duration: `${completionsEndTime - completionsStartTime}ms`,
headers: Object.fromEntries(completionsResponse.headers.entries()),
responsePreview: completionsResponseText.substring(0, 500) + (completionsResponseText.length > 500 ? '...' : ''),
completionContent,
});
// Test 3: Using the AI SDK (same as the app)
try {
logger.info(`Testing AI SDK integration with ${normalizedBaseUrl}`);
const sdkStartTime = Date.now();
// This mimics exactly how the app uses the provider
const openaiLike = getOpenAILikeModel(normalizedBaseUrl, effectiveApiKey, model);
const result = await generateText({
messages: [
{
role: 'user',
content: 'Hello from Buildify AI SDK test. Please respond with a very short greeting.',
},
],
model: openaiLike,
maxTokens: 100,
});
const sdkEndTime = Date.now();
results.tests.push({
name: 'AI SDK Integration',
success: true,
duration: `${sdkEndTime - sdkStartTime}ms`,
result: result,
});
} catch (error) {
results.success = false;
results.tests.push({
name: 'AI SDK Integration',
success: false,
error: error instanceof Error ? error.message : String(error),
stack: error instanceof Error ? error.stack : undefined,
});
}
return addCorsHeaders(json(results));
} catch (error) {
results.success = false;
results.error = error instanceof Error ? error.message : String(error);
results.errorStack = error instanceof Error ? error.stack : undefined;
return addCorsHeaders(json(results, { status: 500 }));
}
}
// Handle POST requests for more complex testing
export async function action({ request, context }: ActionFunctionArgs) {
if (request.method === 'OPTIONS') {
return addCorsHeaders(new Response(null, { status: 204 }));
}
try {
const { apiKey, baseUrl, model, messages, system } = await request.json();
// Get API key from cookies if not provided in request body
let effectiveApiKey = apiKey;
if (!effectiveApiKey) {
const cookieHeader = request.headers.get('Cookie');
const apiKeys = getApiKeysFromCookie(cookieHeader);
effectiveApiKey = apiKeys?.['BayerMGA'];
}
if (!effectiveApiKey) {
return addCorsHeaders(
json(
{
success: false,
error: 'No API key provided in request body or cookies',
},
{ status: 400 },
),
);
}
// Normalize base URL
const normalizedBaseUrl = (baseUrl || 'https://chat.int.bayer.com/api/v2').endsWith('/')
? (baseUrl || 'https://chat.int.bayer.com/api/v2').slice(0, -1)
: (baseUrl || 'https://chat.int.bayer.com/api/v2');
const effectiveModel = model || 'gpt-4o-mini';
logger.info(`Testing AI SDK integration with custom messages: ${normalizedBaseUrl}`);
// Create OpenAI-like model instance (same as the provider)
const openaiLike = getOpenAILikeModel(normalizedBaseUrl, effectiveApiKey, effectiveModel);
// Generate text using the AI SDK
const result = await generateText({
system: system || undefined,
messages: messages || [
{
role: 'user',
content: 'Hello from Buildify custom test. Please respond with a very short greeting.',
},
],
model: openaiLike,
maxTokens: 500,
});
return addCorsHeaders(
json({
success: true,
baseUrl: normalizedBaseUrl,
model: effectiveModel,
result,
}),
);
} catch (error) {
logger.error(`Error in debug action: ${error instanceof Error ? error.message : String(error)}`);
return addCorsHeaders(
json(
{
success: false,
error: error instanceof Error ? error.message : String(error),
stack: error instanceof Error ? error.stack : undefined,
},
{ status: 500 },
),
);
}
}