photum/services/genaiService.ts
João Vitor 1caeddc72c feat: Initialize PhotumManager project structure
This commit sets up the foundational project structure for PhotumManager. It includes:
- Initializing a new React project with Vite.
- Configuring essential dependencies such as React, Lucide React, and the Google Generative AI SDK.
- Setting up TypeScript and Vite configurations for optimal development.
- Defining core application metadata and initial type definitions for users and events.
- Establishing basic styling and font configurations in `index.html` with Tailwind CSS.
- Adding a `.gitignore` file to manage project dependencies and build artifacts.
- Updating the README with instructions for local development.
2025-11-25 11:02:25 -03:00

99 lines
3.1 KiB
TypeScript

import { GoogleGenAI } from "@google/genai";
const ai = new GoogleGenAI({ apiKey: process.env.API_KEY });
export interface GeoResult {
street: string;
number: string;
city: string;
state: string;
zip: string;
description: string;
mapLink?: string;
}
export const searchLocationWithGemini = async (query: string): Promise<GeoResult[]> => {
if (!query || query.length < 3) return [];
try {
// Attempt to get user location for better context
let userLocation: { latitude: number; longitude: number } | undefined;
try {
const position = await new Promise<GeolocationPosition>((resolve, reject) => {
navigator.geolocation.getCurrentPosition(resolve, reject, { timeout: 2000 });
});
userLocation = {
latitude: position.coords.latitude,
longitude: position.coords.longitude
};
} catch (e) {
// Ignore geolocation errors, proceed without it
}
const response = await ai.models.generateContent({
model: 'gemini-2.5-flash',
contents: `Find the specific address details for the location query: "${query}".
You MUST return the address components in the following format (one per line):
Description: [A concise formatted address]
Street: [Street Name]
Number: [Street Number, if available]
City: [City Name]
State: [State Code, e.g., SP, RJ]
Zip: [Postal Code]
If a specific component is not found, leave it empty after the colon.`,
config: {
tools: [{ googleMaps: {} }],
toolConfig: userLocation ? {
retrievalConfig: {
latLng: {
latitude: userLocation.latitude,
longitude: userLocation.longitude
}
}
} : undefined
}
});
const text = response.text || "";
// Helper parser
const parseField = (key: string) => {
const regex = new RegExp(`${key}:\\s*(.*)`, 'i');
const match = text.match(regex);
return match ? match[1].trim() : '';
};
// Extract Google Maps URI from grounding metadata
let mapUri = '';
const chunks = response.candidates?.[0]?.groundingMetadata?.groundingChunks || [];
// Look for the Web URI in the chunks (Google Maps grounding often returns this)
for (const chunk of chunks) {
if (chunk.web?.uri && (chunk.web.uri.includes('google.com/maps') || chunk.web.uri.includes('maps.google.com'))) {
mapUri = chunk.web.uri;
break;
}
}
const result: GeoResult = {
street: parseField('Street'),
number: parseField('Number'),
city: parseField('City'),
state: parseField('State'),
zip: parseField('Zip'),
description: parseField('Description') || text.split('\n')[0], // Fallback to first line
mapLink: mapUri
};
// Filter out bad results (empty city/state usually means it failed to identify a place)
if (!result.city && !result.street) return [];
return [result];
} catch (error) {
console.error("Gemini Search Error:", error);
return [];
}
};