correções e bugs
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { ChevronDown, UserCog } from "lucide-react";
|
import { ChevronDown, UserCog, Loader2 } from "lucide-react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
@@ -15,7 +15,11 @@ import {
|
|||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { useState } from "react";
|
import { useState, useEffect } from "react";
|
||||||
|
import { fetchModelsFromDatabase, ModelConfig } from "@/config/models";
|
||||||
|
|
||||||
|
// Texto padrão da personalidade (mesmo valor usado em ChatView)
|
||||||
|
const DEFAULT_SYSTEM_PROMPT = "Você é um assistente útil e prestativo.";
|
||||||
|
|
||||||
interface ChatHeaderProps {
|
interface ChatHeaderProps {
|
||||||
showModelSelector?: boolean;
|
showModelSelector?: boolean;
|
||||||
@@ -29,25 +33,30 @@ export const ChatHeader = ({
|
|||||||
showModelSelector = false,
|
showModelSelector = false,
|
||||||
selectedModel = "GPT-4o",
|
selectedModel = "GPT-4o",
|
||||||
onModelChange,
|
onModelChange,
|
||||||
systemPrompt = "Você é um assistente útil e prestativo.",
|
systemPrompt = DEFAULT_SYSTEM_PROMPT,
|
||||||
onSystemPromptChange
|
onSystemPromptChange
|
||||||
}: ChatHeaderProps) => {
|
}: ChatHeaderProps) => {
|
||||||
const [isPersonalityOpen, setIsPersonalityOpen] = useState(false);
|
const [isPersonalityOpen, setIsPersonalityOpen] = useState(false);
|
||||||
const [tempSystemPrompt, setTempSystemPrompt] = useState(systemPrompt);
|
const [tempSystemPrompt, setTempSystemPrompt] = useState(systemPrompt);
|
||||||
|
const [models, setModels] = useState<ModelConfig[]>([]);
|
||||||
|
const [isLoadingModels, setIsLoadingModels] = useState(true);
|
||||||
|
|
||||||
const models = [
|
// Carrega modelos da API ao montar o componente
|
||||||
"GPT-4.1",
|
useEffect(() => {
|
||||||
"GPT-4o",
|
const loadModels = async () => {
|
||||||
"GPT-5 Mini",
|
setIsLoadingModels(true);
|
||||||
"Gemini 2.0 Flash",
|
try {
|
||||||
"Claude Sonnet 4.5",
|
const modelsFromAPI = await fetchModelsFromDatabase();
|
||||||
"DeepSeek V3.2 Chat",
|
setModels(modelsFromAPI);
|
||||||
"DeepSeek V3.2 Reasoner",
|
} catch (error) {
|
||||||
"Gemini 2.5 Flash",
|
console.error('Erro ao carregar modelos:', error);
|
||||||
"Gemini 2.5 Flash-Lite",
|
} finally {
|
||||||
"Claude Haiku 4.5",
|
setIsLoadingModels(false);
|
||||||
"GPT-4o Mini",
|
}
|
||||||
];
|
};
|
||||||
|
|
||||||
|
loadModels();
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleSavePersonality = () => {
|
const handleSavePersonality = () => {
|
||||||
if (onSystemPromptChange) {
|
if (onSystemPromptChange) {
|
||||||
@@ -67,19 +76,37 @@ export const ChatHeader = ({
|
|||||||
{showModelSelector && onModelChange && (
|
{showModelSelector && onModelChange && (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button variant="outline" className="gap-1 md:gap-2 glass-effect text-xs md:text-sm">
|
<Button
|
||||||
<span className="font-medium truncate max-w-[80px] md:max-w-none">{selectedModel}</span>
|
variant="outline"
|
||||||
<ChevronDown className="w-3 h-3 md:w-4 md:h-4" />
|
className="gap-1 md:gap-2 glass-effect text-xs md:text-sm"
|
||||||
|
disabled={isLoadingModels}
|
||||||
|
>
|
||||||
|
{isLoadingModels ? (
|
||||||
|
<>
|
||||||
|
<Loader2 className="w-3 h-3 md:w-4 md:h-4 animate-spin" />
|
||||||
|
<span className="font-medium">Carregando...</span>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<span className="font-medium truncate max-w-[80px] md:max-w-none">{selectedModel}</span>
|
||||||
|
<ChevronDown className="w-3 h-3 md:w-4 md:h-4" />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="start" className="glass-effect bg-popover border-border z-50">
|
<DropdownMenuContent align="start" className="glass-effect bg-popover border-border z-50">
|
||||||
{models.map((model) => (
|
{models.map((model) => (
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
key={model}
|
key={model.id}
|
||||||
className="cursor-pointer"
|
className="cursor-pointer"
|
||||||
onClick={() => onModelChange(model)}
|
onClick={() => onModelChange(model.name)}
|
||||||
>
|
>
|
||||||
{model}
|
<div className="flex flex-col">
|
||||||
|
<span className="font-medium">{model.name}</span>
|
||||||
|
{model.description && (
|
||||||
|
<span className="text-xs text-muted-foreground">{model.description}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
))}
|
))}
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ import { chatService, ChatRecord, MessageRecord } from "@/services/chat";
|
|||||||
import { useToast } from "@/hooks/use-toast";
|
import { useToast } from "@/hooks/use-toast";
|
||||||
import { getModelId } from "@/config/models";
|
import { getModelId } from "@/config/models";
|
||||||
|
|
||||||
|
// Texto padrão da personalidade (usado para novos chats)
|
||||||
|
const DEFAULT_SYSTEM_PROMPT = "Você é um assistente útil e prestativo.";
|
||||||
|
|
||||||
interface Message {
|
interface Message {
|
||||||
id: string;
|
id: string;
|
||||||
role: "user" | "assistant";
|
role: "user" | "assistant";
|
||||||
@@ -26,7 +29,8 @@ export const ChatView = () => {
|
|||||||
const [selectedModel, setSelectedModel] = useState("GPT-4o");
|
const [selectedModel, setSelectedModel] = useState("GPT-4o");
|
||||||
// Inicia com "0" - será atualizado com o chat_id real após primeira resposta da API
|
// Inicia com "0" - será atualizado com o chat_id real após primeira resposta da API
|
||||||
const [currentChatId, setCurrentChatId] = useState("0");
|
const [currentChatId, setCurrentChatId] = useState("0");
|
||||||
const [systemPrompt, setSystemPrompt] = useState("Você é um assistente útil e prestativo.");
|
// Inicia com texto padrão - será atualizado ao carregar chat existente
|
||||||
|
const [systemPrompt, setSystemPrompt] = useState(DEFAULT_SYSTEM_PROMPT);
|
||||||
const [messages, setMessages] = useState<Message[]>([
|
const [messages, setMessages] = useState<Message[]>([
|
||||||
{
|
{
|
||||||
id: "1",
|
id: "1",
|
||||||
@@ -74,6 +78,10 @@ export const ChatView = () => {
|
|||||||
const handleNewChat = () => {
|
const handleNewChat = () => {
|
||||||
// Reseta para "0" - novo chat sempre começa com chat_id "0"
|
// Reseta para "0" - novo chat sempre começa com chat_id "0"
|
||||||
setCurrentChatId("0");
|
setCurrentChatId("0");
|
||||||
|
|
||||||
|
// Reseta a personalidade para o texto padrão
|
||||||
|
setSystemPrompt(DEFAULT_SYSTEM_PROMPT);
|
||||||
|
|
||||||
setMessages([
|
setMessages([
|
||||||
{
|
{
|
||||||
id: "1",
|
id: "1",
|
||||||
@@ -91,6 +99,10 @@ export const ChatView = () => {
|
|||||||
// Carrega um chat existente do banco de dados
|
// Carrega um chat existente do banco de dados
|
||||||
setCurrentChatId(chat.id);
|
setCurrentChatId(chat.id);
|
||||||
|
|
||||||
|
// Atualiza a personalidade com o valor do banco de dados
|
||||||
|
// Se não tiver personalidade salva, usa o texto padrão
|
||||||
|
setSystemPrompt(chat.personalidade || DEFAULT_SYSTEM_PROMPT);
|
||||||
|
|
||||||
// Limpa mensagens enquanto carrega
|
// Limpa mensagens enquanto carrega
|
||||||
setMessages([]);
|
setMessages([]);
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
@@ -99,14 +111,12 @@ export const ChatView = () => {
|
|||||||
// Busca mensagens do chat na API
|
// Busca mensagens do chat na API
|
||||||
const messagesFromAPI = await chatService.getChatMessages(chat.id);
|
const messagesFromAPI = await chatService.getChatMessages(chat.id);
|
||||||
|
|
||||||
console.log('Mensagens carregadas da API:', messagesFromAPI);
|
|
||||||
|
|
||||||
// Converte MessageRecord para Message
|
// Converte MessageRecord para Message
|
||||||
const loadedMessages: Message[] = messagesFromAPI.map((msg: MessageRecord) => ({
|
const loadedMessages: Message[] = messagesFromAPI.map((msg: MessageRecord) => ({
|
||||||
id: msg.id,
|
id: msg.id,
|
||||||
role: msg.role,
|
role: msg.role,
|
||||||
content: msg.content,
|
content: msg.content,
|
||||||
model: undefined, // model_id vem como número, não temos mapeamento reverso
|
model: msg.model_name, // Nome do modelo retornado pela API
|
||||||
attachments: msg.has_attachments ? [] : undefined, // Não temos detalhes dos anexos no GET
|
attachments: msg.has_attachments ? [] : undefined, // Não temos detalhes dos anexos no GET
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -236,7 +246,7 @@ export const ChatView = () => {
|
|||||||
|
|
||||||
{/* Main Chat Area */}
|
{/* Main Chat Area */}
|
||||||
<div className="flex-1 flex flex-col w-full">
|
<div className="flex-1 flex flex-col w-full">
|
||||||
<ChatHeader
|
<ChatHeader
|
||||||
showModelSelector={true}
|
showModelSelector={true}
|
||||||
selectedModel={selectedModel}
|
selectedModel={selectedModel}
|
||||||
onModelChange={setSelectedModel}
|
onModelChange={setSelectedModel}
|
||||||
|
|||||||
+42
-16
@@ -9,6 +9,21 @@ export interface ModelConfig {
|
|||||||
description?: string; // Descrição opcional do modelo
|
description?: string; // Descrição opcional do modelo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface para modelo retornado pela API do banco de dados
|
||||||
|
*/
|
||||||
|
export interface ModelIA {
|
||||||
|
id: number;
|
||||||
|
provider_id: number;
|
||||||
|
name: string;
|
||||||
|
model_identifier: string;
|
||||||
|
cost_input_per_million: string;
|
||||||
|
cost_output_per_million: string;
|
||||||
|
is_active: number;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lista de modelos disponíveis
|
* Lista de modelos disponíveis
|
||||||
* NOTA: Estes dados correspondem à tabela de modelos do banco de dados.
|
* NOTA: Estes dados correspondem à tabela de modelos do banco de dados.
|
||||||
@@ -112,21 +127,32 @@ export function getModelNames(): string[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FUTURA INTEGRAÇÃO COM BANCO DE DADOS
|
* Busca modelos de IA disponíveis no banco de dados
|
||||||
*
|
*
|
||||||
* Quando a API estiver pronta, substituir AVAILABLE_MODELS por chamada à API:
|
* @returns Promise com array de modelos ativos
|
||||||
*
|
|
||||||
* export async function fetchModelsFromDatabase(): Promise<ModelConfig[]> {
|
|
||||||
* const response = await apiService.get('/api/models');
|
|
||||||
* return response.data.map((model: any) => ({
|
|
||||||
* name: model.name,
|
|
||||||
* id: model.id.toString(),
|
|
||||||
* description: model.description || '',
|
|
||||||
* }));
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* Nos componentes, usar:
|
|
||||||
* - useEffect para carregar modelos na montagem
|
|
||||||
* - useState para armazenar lista de modelos
|
|
||||||
* - Loading state durante o fetch
|
|
||||||
*/
|
*/
|
||||||
|
export async function fetchModelsFromDatabase(): Promise<ModelConfig[]> {
|
||||||
|
try {
|
||||||
|
// Importa dinamicamente para evitar circular dependency
|
||||||
|
const { apiService } = await import('@/services/api');
|
||||||
|
|
||||||
|
const response = await apiService.get<ModelIA[]>('/webhook/codex/get_models_ia');
|
||||||
|
|
||||||
|
console.log('Modelos carregados da API:', response.data);
|
||||||
|
|
||||||
|
// Filtra apenas modelos ativos e converte para ModelConfig
|
||||||
|
return response.data
|
||||||
|
.filter(model => model.is_active === 1)
|
||||||
|
.map((model: ModelIA) => ({
|
||||||
|
name: model.name,
|
||||||
|
id: model.id.toString(),
|
||||||
|
description: `${model.model_identifier}`,
|
||||||
|
}));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erro ao buscar modelos da API:', error);
|
||||||
|
|
||||||
|
// Fallback para modelos hardcoded em caso de erro
|
||||||
|
console.warn('Usando modelos hardcoded como fallback');
|
||||||
|
return AVAILABLE_MODELS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ export interface ChatRecord {
|
|||||||
folder_id: string | null;
|
folder_id: string | null;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
|
personalidade: string; // Personalidade/prompt do sistema salvo no banco
|
||||||
estabelecimento_id: number;
|
estabelecimento_id: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,6 +100,7 @@ export interface MessageRecord {
|
|||||||
role: 'user' | 'assistant';
|
role: 'user' | 'assistant';
|
||||||
content: string;
|
content: string;
|
||||||
model_id: number;
|
model_id: number;
|
||||||
|
model_name?: string; // Nome do modelo retornado pela API
|
||||||
has_attachments: number;
|
has_attachments: number;
|
||||||
input_tokens: number;
|
input_tokens: number;
|
||||||
output_tokens: number;
|
output_tokens: number;
|
||||||
|
|||||||
Reference in New Issue
Block a user