Auto commit: 2025-12-02T15:04:45.684Z

This commit is contained in:
Flatlogic Bot 2025-12-02 15:04:45 +00:00
parent 3c98edbcb2
commit e06d66bb12
3 changed files with 203 additions and 16 deletions

View File

@ -3,6 +3,8 @@ ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
session_start();
header('Content-Type: application/json');
require_once __DIR__ . '/../ai/LocalAIApi.php';
@ -11,18 +13,30 @@ require_once __DIR__ . '/../includes/pexels.php';
$requestBody = file_get_contents('php://input');
$data = json_decode($requestBody, true);
$query = $data['query'] ?? '';
$persona = $data['persona'] ?? 'travel_agent';
if (empty($query)) {
echo json_encode(['error' => 'Query is empty']);
exit;
}
$conversation = $_SESSION['conversation'] ?? [];
$conversation[] = ['role' => 'user', 'content' => $query];
$persona_prompts = [
'travel_agent' => 'You are a helpful travel agent specializing in Poland.',
'historian' => 'You are a historian specializing in Polish history. Your responses should be informative, detailed, and focus on the historical significance of places and events.',
'foodie' => 'You are a food critic and blogger with a passion for Polish cuisine. Your responses should be enthusiastic, descriptive, and focus on food, restaurants, and culinary experiences.',
'adventurer' => 'You are an adventure travel guide who loves exploring the wild side of Poland. Your responses should be exciting, energetic, and focus on outdoor activities, hiking, and unique experiences.'
];
$system_prompt = $persona_prompts[$persona] . ' The user is looking for: ' . $query . '. Provide a travel suggestion with a title, a short description, a 3-day itinerary, and the latitude and longitude of the location. Your response must be in JSON format, like this: {"title": "...", "description": "...", "itinerary": [{"day": 1, "activities": ["...", "..."]}, {"day": 2, "activities": ["...", "..."]}, {"day": 3, "activities": ["...", "..."]}], "location": {"lat": ..., "lng": ...}}';
$messages = array_merge([['role' => 'system', 'content' => $system_prompt]], $conversation);
$resp = LocalAIApi::createResponse(
[
'input' => [
['role' => 'system', 'content' => 'You are a travel agent specializing in Poland. The user is looking for: ' . $query . '. Provide a travel suggestion with a title and a description. Your response must be in JSON format, like this: {"title": "...", "description": "..."}'],
['role' => 'user', 'content' => $query],
],
'input' => $messages
]
);
@ -35,11 +49,17 @@ if (!empty($resp['success'])) {
// This is a fallback mechanism
$title = "AI Generated Response";
$description = $text;
$itinerary = [];
} else {
$title = $aiResponse['title'] ?? 'AI Generated Response';
$description = $aiResponse['description'] ?? 'No description available.';
$itinerary = $aiResponse['itinerary'] ?? [];
$location = $aiResponse['location'] ?? null;
}
$conversation[] = ['role' => 'assistant', 'content' => $text];
$_SESSION['conversation'] = $conversation;
$pexelsUrl = 'https://api.pexels.com/v1/search?query=' . urlencode($title) . '&orientation=landscape&per_page=1&page=1';
$pexelsData = pexels_get($pexelsUrl);
@ -52,6 +72,8 @@ if (!empty($resp['success'])) {
echo json_encode([
'title' => $title,
'description' => $description,
'itinerary' => $itinerary,
'location' => $location,
'image' => $imageUrl
]);
} else {

View File

@ -1,27 +1,119 @@
document.getElementById('suggestion-form').addEventListener('submit', function(e) {
e.preventDefault();
const query = document.getElementById('query').value;
const persona = document.getElementById('persona').value;
const suggestionCard = document.getElementById('suggestion-card');
const loading = document.getElementById('loading');
const errorMessage = document.getElementById('error-message');
const errorText = document.getElementById('error-text');
const suggestionImage = document.getElementById('suggestion-image');
const suggestionTitle = document.getElementById('suggestion-title');
const suggestionText = document.getElementById('suggestion-text');
const funFact = document.getElementById('fun-fact');
const historySection = document.getElementById('history-section');
const historyList = document.getElementById('history-list');
const itinerarySection = document.getElementById('itinerary-section');
const itineraryList = document.getElementById('itinerary-list');
const mapElement = document.getElementById('map');
const suggestionHistory = [];
let map;
const funFacts = [
"Poland is home to the world's largest castle, Malbork Castle.",
"The name \"Poland\" (Polska) originates from the Polanie tribe, meaning \"people living in open fields.\"",
"Poland has 17 Nobel Prize winners, including four for Peace and five for Literature.",
"The Polish alphabet consists of 32 letters.",
"Marie Curie, the pioneering scientist, was Polish.",
"Poland is the world's biggest exporter of amber.",
"The city of Wrocław has a network of over 300 small bronze statues of dwarves (krasnale).",
"Poland's Białowieża Forest is one of the last and largest remaining parts of the immense primeval forest that once stretched across the European Plain."
];
let funFactInterval;
suggestionCard.style.display = 'none';
errorMessage.style.display = 'none';
loading.style.display = 'block';
mapElement.style.display = 'none';
function showRandomFunFact() {
const randomIndex = Math.floor(Math.random() * funFacts.length);
funFact.textContent = funFacts[randomIndex];
}
showRandomFunFact();
funFactInterval = setInterval(showRandomFunFact, 3000);
function showError(message) {
errorText.textContent = message;
errorMessage.style.display = 'block';
loading.style.display = 'none';
clearInterval(funFactInterval);
}
function updateHistory() {
historyList.innerHTML = '';
suggestionHistory.slice(0, 5).forEach(item => {
const a = document.createElement('a');
a.href = '#';
a.className = 'list-group-item list-group-item-action';
a.textContent = item.title;
a.onclick = (e) => {
e.preventDefault();
suggestionImage.src = item.image;
suggestionTitle.textContent = item.title;
suggestionText.textContent = item.description;
if (item.itinerary) {
itineraryList.innerHTML = '';
item.itinerary.forEach(day => {
const li = document.createElement('li');
li.className = 'list-group-item';
li.innerHTML = `<b>Day ${day.day}:</b> ${day.activities.join(', ')}`;
itineraryList.appendChild(li);
});
itinerarySection.style.display = 'block';
} else {
itinerarySection.style.display = 'none';
}
if (item.location && item.location.lat && item.location.lng) {
mapElement.style.display = 'block';
if (!map) {
map = L.map('map').setView([item.location.lat, item.location.lng], 13);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
} else {
map.setView([item.location.lat, item.location.lng], 13);
}
L.marker([item.location.lat, item.location.lng]).addTo(map)
.bindPopup(item.title)
.openPopup();
} else {
mapElement.style.display = 'none';
}
suggestionCard.style.display = 'block';
};
historyList.appendChild(a);
});
historySection.style.display = suggestionHistory.length > 0 ? 'block' : 'none';
}
fetch('api/index.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ query: query })
body: JSON.stringify({ query: query, persona: persona })
})
.then(response => response.json())
.then(data => {
clearInterval(funFactInterval);
if (data.error) {
alert(data.error);
loading.style.display = 'none';
showError(data.error);
return;
}
@ -29,12 +121,65 @@ document.getElementById('suggestion-form').addEventListener('submit', function(e
suggestionTitle.textContent = data.title;
suggestionText.textContent = data.description;
if (data.itinerary) {
itineraryList.innerHTML = '';
data.itinerary.forEach(day => {
const li = document.createElement('li');
li.className = 'list-group-item';
li.innerHTML = `<b>Day ${day.day}:</b> ${day.activities.join(', ')}`;
itineraryList.appendChild(li);
});
itinerarySection.style.display = 'block';
} else {
itinerarySection.style.display = 'none';
}
if (data.location && data.location.lat && data.location.lng) {
mapElement.style.display = 'block';
if (!map) {
map = L.map('map').setView([data.location.lat, data.location.lng], 13);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
} else {
map.setView([data.location.lat, data.location.lng], 13);
}
L.marker([data.location.lat, data.location.lng]).addTo(map)
.bindPopup(data.title)
.openPopup();
} else {
mapElement.style.display = 'none';
}
suggestionHistory.unshift(data);
updateHistory();
loading.style.display = 'none';
suggestionCard.style.display = 'block';
})
.catch(error => {
console.error('Error:', error);
loading.style.display = 'none';
alert('An error occurred while fetching the suggestion.');
showError('An error occurred while fetching the suggestion.');
});
});
});
document.getElementById('lucky-button').addEventListener('click', function() {
const queries = [
"a hidden gem in Warsaw",
"the best pierogi in Krakow",
"a beautiful beach on the Baltic coast",
"a challenging hike in the Tatra Mountains",
"a quirky museum in Gdansk"
];
const randomQuery = queries[Math.floor(Math.random() * queries.length)];
document.getElementById('query').value = randomQuery;
document.getElementById('suggestion-form').dispatchEvent(new Event('submit'));
});
// Optional: Add event listener to close the error message
const errorAlert = document.getElementById('error-message');
if (errorAlert) {
errorAlert.querySelector('.btn-close').addEventListener('click', function () {
errorAlert.style.display = 'none';
});
}

View File

@ -1,10 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI Travel Agent</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin=""/>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&display=swap" rel="stylesheet">
@ -26,11 +21,19 @@
<div class="card-body">
<form id="suggestion-form">
<div class="mb-4 text-center">
<label for="persona" class="form-label fs-5 mb-2">Choose your guide:</label>
<select class="form-select form-select-lg mb-3" id="persona">
<option value="travel_agent">Travel Agent</option>
<option value="historian">Historian</option>
<option value="foodie">Foodie</option>
<option value="adventurer">Adventurer</option>
</select>
<label for="query" class="form-label fs-4 mb-3">What are you looking for?</label>
<input type="text" class="form-control form-control-lg" id="query" placeholder="e.g., a cozy cafe in Krakow">
</div>
<div class="text-center">
<button type="submit" class="btn btn-primary btn-lg px-5">Get Suggestion</button>
<button type="button" id="lucky-button" class="btn btn-secondary btn-lg px-5">I'm Feeling Lucky</button>
</div>
</form>
</div>
@ -40,23 +43,40 @@
<div class="row justify-content-center mt-4">
<div class="col-md-8">
<div id="error-message" class="alert alert-danger alert-dismissible fade show" role="alert" style="display: none;">
<span id="error-text"></span>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<div id="loading" class="text-center">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<p>Thinking...</p>
<p id="fun-fact" class="text-muted mt-2"></p>
</div>
<div class="card" id="suggestion-card">
<img id="suggestion-image" src="" class="card-img-top" alt="Suggestion Image">
<div class="card-body">
<h5 class="card-title" id="suggestion-title"></h5>
<p class="card-text" id="suggestion-text"></p>
<div id="itinerary-section" style="display: none;">
<h6>Itinerary</h6>
<ul id="itinerary-list" class="list-group"></ul>
</div>
</div>
</div>
<div id="map" style="height: 400px; display: none;" class="mt-4"></div>
<div id="history-section" class="mt-4" style="display: none;">
<h4>History</h4>
<div id="history-list" class="list-group"></div>
</div>
</div>
</div>
</div>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
</body>
</html>