import type { GooglePagespeedAnalyzeParams, GooglePagespeedAnalyzeResponse, } from '@/tools/google_pagespeed/types' import type { ToolConfig } from '@/tools/types' export const analyzeTool: ToolConfig = { id: 'google_pagespeed_analyze', name: 'Google PageSpeed Analyze', description: 'Analyze a webpage for performance, accessibility, SEO, and best practices using Google PageSpeed Insights.', version: '1.0.0', params: { apiKey: { type: 'string', required: true, visibility: 'user-only', description: 'Google PageSpeed Insights API Key', }, url: { type: 'string', required: true, visibility: 'user-or-llm', description: 'The URL of the webpage to analyze', }, category: { type: 'string', required: false, visibility: 'user-or-llm', description: 'Lighthouse categories to analyze (comma-separated): performance, accessibility, best-practices, seo', }, strategy: { type: 'string', required: false, visibility: 'user-or-llm', description: 'Analysis strategy: desktop or mobile', }, locale: { type: 'string', required: false, visibility: 'user-or-llm', description: 'Locale for results (e.g., en, fr, de)', }, }, hosting: { envKeyPrefix: 'GOOGLE_CLOUD_API_KEY', apiKeyParam: 'apiKey', byokProviderId: 'google_cloud', pricing: { type: 'per_request', cost: 0, }, rateLimit: { mode: 'per_request', requestsPerMinute: 20, }, }, request: { url: (params) => { const url = new URL('https://www.googleapis.com/pagespeedonline/v5/runPagespeed') url.searchParams.append('url', params.url.trim()) url.searchParams.append('key', params.apiKey) if (params.category) { const categories = params.category.split(',').map((c) => c.trim()) for (const cat of categories) { url.searchParams.append('category', cat) } } else { url.searchParams.append('category', 'performance') url.searchParams.append('category', 'accessibility') url.searchParams.append('category', 'best-practices') url.searchParams.append('category', 'seo') } if (params.strategy) { url.searchParams.append('strategy', params.strategy) } if (params.locale) { url.searchParams.append('locale', params.locale) } return url.toString() }, method: 'GET', headers: () => ({ Accept: 'application/json', }), }, transformResponse: async (response: Response) => { const data = await response.json() if (!response.ok) { throw new Error(data.error?.message ?? 'Failed to analyze page') } const lighthouse = data.lighthouseResult ?? {} const categories = lighthouse.categories ?? {} const audits = lighthouse.audits ?? {} const loadingExperience = data.loadingExperience ?? {} return { success: true, output: { finalUrl: data.id ?? null, performanceScore: categories.performance?.score ?? null, accessibilityScore: categories.accessibility?.score ?? null, bestPracticesScore: categories['best-practices']?.score ?? null, seoScore: categories.seo?.score ?? null, firstContentfulPaint: audits['first-contentful-paint']?.displayValue ?? null, firstContentfulPaintMs: audits['first-contentful-paint']?.numericValue ?? null, largestContentfulPaint: audits['largest-contentful-paint']?.displayValue ?? null, largestContentfulPaintMs: audits['largest-contentful-paint']?.numericValue ?? null, totalBlockingTime: audits['total-blocking-time']?.displayValue ?? null, totalBlockingTimeMs: audits['total-blocking-time']?.numericValue ?? null, cumulativeLayoutShift: audits['cumulative-layout-shift']?.displayValue ?? null, cumulativeLayoutShiftValue: audits['cumulative-layout-shift']?.numericValue ?? null, speedIndex: audits['speed-index']?.displayValue ?? null, speedIndexMs: audits['speed-index']?.numericValue ?? null, interactive: audits.interactive?.displayValue ?? null, interactiveMs: audits.interactive?.numericValue ?? null, overallCategory: loadingExperience.overall_category ?? null, analysisTimestamp: data.analysisUTCTimestamp ?? null, lighthouseVersion: lighthouse.lighthouseVersion ?? null, }, } }, outputs: { finalUrl: { type: 'string', description: 'The final URL after redirects', optional: true, }, performanceScore: { type: 'number', description: 'Performance category score (0-1)', optional: true, }, accessibilityScore: { type: 'number', description: 'Accessibility category score (0-1)', optional: true, }, bestPracticesScore: { type: 'number', description: 'Best Practices category score (0-1)', optional: true, }, seoScore: { type: 'number', description: 'SEO category score (0-1)', optional: true, }, firstContentfulPaint: { type: 'string', description: 'Time to First Contentful Paint (display value)', optional: true, }, firstContentfulPaintMs: { type: 'number', description: 'Time to First Contentful Paint in milliseconds', optional: true, }, largestContentfulPaint: { type: 'string', description: 'Time to Largest Contentful Paint (display value)', optional: true, }, largestContentfulPaintMs: { type: 'number', description: 'Time to Largest Contentful Paint in milliseconds', optional: true, }, totalBlockingTime: { type: 'string', description: 'Total Blocking Time (display value)', optional: true, }, totalBlockingTimeMs: { type: 'number', description: 'Total Blocking Time in milliseconds', optional: true, }, cumulativeLayoutShift: { type: 'string', description: 'Cumulative Layout Shift (display value)', optional: true, }, cumulativeLayoutShiftValue: { type: 'number', description: 'Cumulative Layout Shift numeric value', optional: true, }, speedIndex: { type: 'string', description: 'Speed Index (display value)', optional: true, }, speedIndexMs: { type: 'number', description: 'Speed Index in milliseconds', optional: true, }, interactive: { type: 'string', description: 'Time to Interactive (display value)', optional: true, }, interactiveMs: { type: 'number', description: 'Time to Interactive in milliseconds', optional: true, }, overallCategory: { type: 'string', description: 'Overall loading experience category (FAST, AVERAGE, SLOW, or NONE)', optional: true, }, analysisTimestamp: { type: 'string', description: 'UTC timestamp of the analysis', optional: true, }, lighthouseVersion: { type: 'string', description: 'Version of Lighthouse used for the analysis', optional: true, }, }, }