First Commit
This commit is contained in:
Executable
+186
@@ -0,0 +1,186 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('header', 'Chat com IA')
|
||||
|
||||
@section('breadcrumbs')
|
||||
@php
|
||||
$breadcrumbs = [
|
||||
'Chat com IA' => null
|
||||
];
|
||||
@endphp
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header py-3 d-flex justify-content-between align-items-center">
|
||||
<h6 class="m-0 fw-bold text-primary">Conversa</h6>
|
||||
<div>
|
||||
<form method="POST" action="{{ route('chat.reset') }}" class="d-inline" onsubmit="return confirm('Tem certeza que deseja reiniciar a conversa?')">
|
||||
@csrf
|
||||
<button type="submit" class="btn btn-sm btn-outline-secondary me-2">
|
||||
<i class="bi bi-arrow-clockwise me-1"></i> Reiniciar
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="chat-messages" class="border rounded p-3 mb-3 bg-light" style="min-height: 400px; max-height: 600px; overflow-y: auto;">
|
||||
@forelse($history as $msg)
|
||||
<div class="mb-3 {{ $msg['role'] === 'user' ? 'text-end' : 'text-start' }}">
|
||||
<div class="d-inline-block p-2 rounded {{ $msg['role'] === 'user' ? 'bg-primary text-white' : 'bg-white border' }}" style="max-width: 80%; white-space: pre-wrap;">
|
||||
{{ $msg['content'] }}
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<p class="text-muted text-center">Nenhuma mensagem ainda. Inicie a conversa!</p>
|
||||
@endforelse
|
||||
</div>
|
||||
|
||||
<form id="chat-form" class="mt-3">
|
||||
@csrf
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6 mb-2">
|
||||
<label for="provider_id" class="form-label">Provider</label>
|
||||
<select class="form-select" id="provider_id" name="provider_id" required>
|
||||
<option value="">-- Selecione --</option>
|
||||
@foreach($providers as $provider)
|
||||
<option value="{{ $provider->id }}" {{ old('provider_id', $defaultProviderId) == $provider->id ? 'selected' : '' }}>
|
||||
{{ $provider->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6 mb-2">
|
||||
<label for="model_id" class="form-label">Modelo</label>
|
||||
<select class="form-select" id="model_id" name="model_id" required disabled>
|
||||
<option value="">-- Selecione um provider primeiro --</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4 mb-2">
|
||||
<label for="temperature" class="form-label">Temperatura</label>
|
||||
<input type="number" class="form-control" id="temperature" name="temperature" value="{{ old('temperature', 0.7) }}" step="0.1" min="0" max="1">
|
||||
</div>
|
||||
<div class="col-md-4 mb-2">
|
||||
<label for="max_tokens" class="form-label">Max Tokens</label>
|
||||
<input type="number" class="form-control" id="max_tokens" name="max_tokens" value="{{ old('max_tokens', 8192) }}" min="1" max="128000">
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<textarea class="form-control" id="message" name="message" rows="1" placeholder="Digite sua mensagem..." required></textarea>
|
||||
<button class="btn btn-primary" type="submit" id="send-btn">
|
||||
<i class="bi bi-send"></i> Enviar
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
const providerSelect = document.getElementById('provider_id');
|
||||
const modelSelect = document.getElementById('model_id');
|
||||
const chatForm = document.getElementById('chat-form');
|
||||
const chatMessages = document.getElementById('chat-messages');
|
||||
const sendBtn = document.getElementById('send-btn');
|
||||
const messageInput = document.getElementById('message');
|
||||
|
||||
providerSelect.addEventListener('change', function() {
|
||||
const providerId = this.value;
|
||||
|
||||
if (!providerId) {
|
||||
modelSelect.innerHTML = '<option value="">-- Selecione um provider primeiro --</option>';
|
||||
modelSelect.disabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(`{{ route('chat.models') }}?provider_id=${providerId}`)
|
||||
.then(response => response.json())
|
||||
.then(models => {
|
||||
modelSelect.innerHTML = '<option value="">-- Selecione um modelo --</option>';
|
||||
models.forEach(model => {
|
||||
const option = document.createElement('option');
|
||||
option.value = model.id;
|
||||
option.textContent = model.name;
|
||||
modelSelect.appendChild(option);
|
||||
});
|
||||
modelSelect.disabled = false;
|
||||
});
|
||||
});
|
||||
|
||||
chatForm.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const message = messageInput.value.trim();
|
||||
if (!message) return;
|
||||
|
||||
appendMessage('user', message);
|
||||
messageInput.value = '';
|
||||
sendBtn.disabled = true;
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('_token', '{{ csrf_token() }}');
|
||||
formData.append('message', message);
|
||||
formData.append('provider_id', providerSelect.value);
|
||||
formData.append('model_id', modelSelect.value);
|
||||
formData.append('temperature', document.getElementById('temperature').value);
|
||||
formData.append('max_tokens', document.getElementById('max_tokens').value);
|
||||
|
||||
fetch('{{ route('chat.send') }}', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
appendMessage('assistant', data.content);
|
||||
} else {
|
||||
appendMessage('assistant', 'Erro: ' + data.error);
|
||||
}
|
||||
sendBtn.disabled = false;
|
||||
})
|
||||
.catch(() => {
|
||||
appendMessage('assistant', 'Erro ao enviar mensagem.');
|
||||
sendBtn.disabled = false;
|
||||
});
|
||||
});
|
||||
|
||||
function appendMessage(role, content) {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'mb-3 ' + (role === 'user' ? 'text-end' : 'text-start');
|
||||
div.innerHTML = '<div class="d-inline-block p-2 rounded ' + (role === 'user' ? 'bg-primary text-white' : 'bg-white border') + '" style="max-width: 80%; white-space: pre-wrap;">' + escapeHtml(content) + '</div>';
|
||||
chatMessages.appendChild(div);
|
||||
chatMessages.scrollTop = chatMessages.scrollHeight;
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
const map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' };
|
||||
return text.replace(/[&<>"']/g, m => map[m]);
|
||||
}
|
||||
|
||||
@if(!empty($history) && old('provider_id'))
|
||||
window.addEventListener('DOMContentLoaded', function() {
|
||||
providerSelect.value = {{ old('provider_id', 1) }};
|
||||
providerSelect.dispatchEvent(new Event('change'));
|
||||
setTimeout(() => {
|
||||
modelSelect.value = {{ old('model_id') }};
|
||||
}, 500);
|
||||
});
|
||||
@else
|
||||
window.addEventListener('DOMContentLoaded', function() {
|
||||
if (providerSelect.value) {
|
||||
providerSelect.dispatchEvent(new Event('change'));
|
||||
setTimeout(() => {
|
||||
modelSelect.value = {{ $defaultModelId }};
|
||||
}, 500);
|
||||
}
|
||||
});
|
||||
@endif
|
||||
</script>
|
||||
@endsection
|
||||
Reference in New Issue
Block a user