✨ Add automated session logging system
Some checks failed
Build and Deploy Docs / build-and-deploy (push) Has been cancelled
Some checks failed
Build and Deploy Docs / build-and-deploy (push) Has been cancelled
- Created logs/ structure (sessions, operations, incidents) - Added session-start/log/end scripts - Installed Git hooks for auto-logging commits/pushes - Added shell integration for zsh - Created CHANGELOG.md - Documented today's session (2026-01-10)
This commit is contained in:
@@ -27,6 +27,7 @@ import { MessengerPage } from './features/messenger/MessengerPage';
|
||||
// Agent Hub
|
||||
import { AgentHubPage } from './features/agentHub/AgentHubPage';
|
||||
import { AgentCabinet } from './features/agentHub/AgentCabinet';
|
||||
import { SofiaChatPage } from './pages/SofiaChatPage';
|
||||
// MicroDAO Console
|
||||
import { MicrodaoListPage } from './features/microdao/MicrodaoListPage';
|
||||
import { MicrodaoConsolePage } from './features/microdao/MicrodaoConsolePage';
|
||||
@@ -69,6 +70,7 @@ function App() {
|
||||
{/* Task 039: Agent Console v2 */}
|
||||
<Route path="/agents" element={<AgentHubPage />} />
|
||||
<Route path="/agents/:agentId" element={<AgentCabinet />} />
|
||||
<Route path="/sofia" element={<SofiaChatPage />} />
|
||||
{/* Legacy Aliases */}
|
||||
<Route path="/agent-hub" element={<AgentHubPage />} />
|
||||
<Route path="/agent/:agentId" element={<AgentCabinet />} />
|
||||
|
||||
@@ -192,3 +192,12 @@ export async function finishPasskeyAuthentication(
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -219,3 +219,12 @@ async function getNode1StatusRealTime(): Promise<Node1Status> {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -52,3 +52,12 @@ export async function getNodes(): Promise<SpaceNode[]> {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -36,3 +36,12 @@ export async function getPlanets(): Promise<SpacePlanet[]> {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -36,3 +36,12 @@ export async function getSpaceEvents(params?: GetSpaceEventsParams): Promise<Spa
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -27,3 +27,12 @@ export function RequireAuth({ children }: RequireAuthProps) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -69,3 +69,12 @@ export const MicroDaoOrchestratorChatWrapper: React.FC<MicroDaoOrchestratorChatW
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -13,3 +13,12 @@ export { TelegramIntegration } from './TelegramIntegration';
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -77,3 +77,12 @@ export function NavigationBreadcrumbs() {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -93,3 +93,12 @@ export function usePasskeyLogin(): UsePasskeyLoginResult {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -86,3 +86,12 @@ export function usePasskeyRegister(): UsePasskeyRegisterResult {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -179,3 +179,12 @@ function Metric({ label, value }: { label: string; value: string }) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -96,3 +96,12 @@ export function useMicroDAOs(): UseMicroDAOsResult {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -20,3 +20,12 @@ export async function getCitySnapshot(): Promise<CitySnapshot> {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -70,3 +70,12 @@ export function CityAgentPanel({ agent }: CityAgentPanelProps) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -75,3 +75,12 @@ export function CityEventsFeed({ events }: CityEventsFeedProps) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -63,3 +63,12 @@ export function CityLayout({ snapshot }: CityLayoutProps) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -112,3 +112,12 @@ export function CityMetricsGrid({ metrics }: CityMetricsGridProps) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -86,3 +86,12 @@ export function CityMicroDAOPanel({ microdao }: CityMicroDAOPanelProps) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -77,3 +77,12 @@ export function CityNodesSummary({ nodes }: CityNodesSummaryProps) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -56,3 +56,12 @@ export function CityQuestPanel({ quests }: CityQuestPanelProps) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -105,3 +105,12 @@ export function CitySectorMap({ snapshot }: CitySectorMapProps) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -54,3 +54,12 @@ export function useCityData() {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -134,3 +134,12 @@ export function useCityWebSocket(options: UseCityWebSocketOptions) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -84,3 +84,12 @@ export interface CityQuestSummary {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -127,3 +127,12 @@ export function MessengerPage() {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -24,3 +24,12 @@ export async function createChannel(data: ChannelCreateInput): Promise<Channel>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -31,3 +31,12 @@ export async function getChannelMessages(
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -26,3 +26,12 @@ export async function getChannels(microdaoId?: string): Promise<Channel[]> {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -27,3 +27,12 @@ export async function sendMessage(
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -44,3 +44,12 @@ export function ChannelHeader({ channel, isConnected }: Props) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -48,3 +48,12 @@ export function ChannelList({ channels, selectedChannelId, onSelectChannel }: Pr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -80,3 +80,12 @@ export function MessageComposer({ onSend, disabled }: Props) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -66,3 +66,12 @@ export function MessageList({ messages, loading }: Props) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -44,3 +44,12 @@ export function useChannels(microdaoId?: string) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -63,3 +63,12 @@ export function useMessages(channelId: string) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -113,3 +113,12 @@ export function useMessagingWebSocket(channelId: string): UseMessagingWebSocketR
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -101,3 +101,12 @@ export interface WebSocketMessage {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -235,3 +235,12 @@ export const CosmosView = memo(Component);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -189,3 +189,12 @@ export function GalaxyView({ scene, onSelect }: GalaxyViewProps) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -220,3 +220,12 @@ export function StarSystemView({ scene, onSelect }: StarSystemViewProps) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -289,3 +289,12 @@ export function ChatDemoPage() {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
268
src/pages/SofiaChatPage.tsx
Normal file
268
src/pages/SofiaChatPage.tsx
Normal file
@@ -0,0 +1,268 @@
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { Bot, Send, Loader2, Sparkles, Trash2, FileText } from 'lucide-react';
|
||||
|
||||
interface Message {
|
||||
id: string;
|
||||
role: 'user' | 'assistant';
|
||||
content: string;
|
||||
timestamp: Date;
|
||||
tokens?: number;
|
||||
}
|
||||
|
||||
export const SofiaChatPage: React.FC = () => {
|
||||
const [messages, setMessages] = useState<Message[]>([]);
|
||||
const [input, setInput] = useState('');
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Sofia backend URL
|
||||
const sofiaUrl = 'http://localhost:8899'; // Потім запустимо FastAPI бекенд
|
||||
|
||||
const scrollToBottom = () => {
|
||||
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
scrollToBottom();
|
||||
}, [messages]);
|
||||
|
||||
const sendMessage = async () => {
|
||||
if (!input.trim() || isLoading) return;
|
||||
|
||||
const userMessage: Message = {
|
||||
id: Date.now().toString(),
|
||||
role: 'user',
|
||||
content: input,
|
||||
timestamp: new Date(),
|
||||
};
|
||||
|
||||
setMessages(prev => [...prev, userMessage]);
|
||||
setInput('');
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const response = await fetch(`${sofiaUrl}/chat`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
message: input,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
const assistantMessage: Message = {
|
||||
id: (Date.now() + 1).toString(),
|
||||
role: 'assistant',
|
||||
content: data.response || data.message || 'No response',
|
||||
timestamp: new Date(),
|
||||
tokens: data.tokens,
|
||||
};
|
||||
|
||||
setMessages(prev => [...prev, assistantMessage]);
|
||||
} catch (err: any) {
|
||||
console.error('Error sending message:', err);
|
||||
setError(err.message || 'Failed to send message');
|
||||
|
||||
// Fallback: show error as assistant message
|
||||
const errorMessage: Message = {
|
||||
id: (Date.now() + 1).toString(),
|
||||
role: 'assistant',
|
||||
content: `❌ Помилка: ${err.message}. Переконайтесь, що Sofia backend запущено.`,
|
||||
timestamp: new Date(),
|
||||
};
|
||||
setMessages(prev => [...prev, errorMessage]);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const clearChat = () => {
|
||||
setMessages([]);
|
||||
setError(null);
|
||||
};
|
||||
|
||||
const handleKeyPress = (e: React.KeyboardEvent) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
sendMessage();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-purple-50 via-white to-pink-50">
|
||||
{/* Header */}
|
||||
<div className="bg-white border-b border-purple-200 shadow-sm">
|
||||
<div className="max-w-5xl mx-auto px-4 py-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-12 h-12 bg-gradient-to-br from-purple-500 to-pink-500 rounded-xl flex items-center justify-center shadow-lg">
|
||||
<Sparkles className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold bg-gradient-to-r from-purple-600 to-pink-600 bg-clip-text text-transparent">
|
||||
Sofia
|
||||
</h1>
|
||||
<p className="text-sm text-gray-600">
|
||||
Chief AI Engineer & R&D Orchestrator
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
onClick={clearChat}
|
||||
className="px-3 py-2 text-gray-600 hover:text-gray-900 hover:bg-gray-100 rounded-lg transition-colors flex items-center gap-2"
|
||||
title="Очистити чат"
|
||||
>
|
||||
<Trash2 className="w-4 h-4" />
|
||||
<span className="text-sm">Очистити</span>
|
||||
</button>
|
||||
<div className="px-3 py-1 bg-purple-100 text-purple-700 rounded-lg text-xs font-medium">
|
||||
qwen2.5-coder:32b
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Chat Area */}
|
||||
<div className="max-w-5xl mx-auto px-4 py-6">
|
||||
<div className="bg-white rounded-2xl shadow-lg border border-purple-100 overflow-hidden">
|
||||
{/* Messages */}
|
||||
<div className="h-[calc(100vh-280px)] overflow-y-auto p-6 space-y-4">
|
||||
{messages.length === 0 && (
|
||||
<div className="flex flex-col items-center justify-center h-full text-center">
|
||||
<div className="w-20 h-20 bg-gradient-to-br from-purple-100 to-pink-100 rounded-full flex items-center justify-center mb-4">
|
||||
<Sparkles className="w-10 h-10 text-purple-500" />
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold text-gray-800 mb-2">
|
||||
Привіт! Я Sofia 👋
|
||||
</h3>
|
||||
<p className="text-gray-600 max-w-md mb-6">
|
||||
Chief AI Engineer в екосистемі DAARION.city. Допомагаю з дослідженнями AI/ML,
|
||||
архітектурою агентських систем та технічними рішеннями.
|
||||
</p>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3 max-w-2xl">
|
||||
<button
|
||||
onClick={() => setInput('Розкажи про свою роль в DAARION')}
|
||||
className="px-4 py-3 bg-purple-50 hover:bg-purple-100 text-purple-700 rounded-lg text-sm text-left transition-colors"
|
||||
>
|
||||
💼 Розкажи про свою роль
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setInput('Які AI моделі найкращі для NLP задач?')}
|
||||
className="px-4 py-3 bg-purple-50 hover:bg-purple-100 text-purple-700 rounded-lg text-sm text-left transition-colors"
|
||||
>
|
||||
🤖 Які моделі для NLP?
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setInput('Як побудувати multi-agent систему?')}
|
||||
className="px-4 py-3 bg-purple-50 hover:bg-purple-100 text-purple-700 rounded-lg text-sm text-left transition-colors"
|
||||
>
|
||||
🏗️ Multi-agent архітектура
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setInput('Поясни різницю між RAG та fine-tuning')}
|
||||
className="px-4 py-3 bg-purple-50 hover:bg-purple-100 text-purple-700 rounded-lg text-sm text-left transition-colors"
|
||||
>
|
||||
📚 RAG vs Fine-tuning
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{messages.map((message) => (
|
||||
<div
|
||||
key={message.id}
|
||||
className={`flex gap-3 ${message.role === 'user' ? 'justify-end' : 'justify-start'}`}
|
||||
>
|
||||
{message.role === 'assistant' && (
|
||||
<div className="w-8 h-8 bg-gradient-to-br from-purple-500 to-pink-500 rounded-lg flex items-center justify-center flex-shrink-0">
|
||||
<Bot className="w-5 h-5 text-white" />
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={`max-w-[70%] px-4 py-3 rounded-2xl ${
|
||||
message.role === 'user'
|
||||
? 'bg-gradient-to-br from-purple-500 to-pink-500 text-white'
|
||||
: 'bg-gray-50 text-gray-900'
|
||||
}`}
|
||||
>
|
||||
<div className="whitespace-pre-wrap break-words">{message.content}</div>
|
||||
<div className="mt-2 flex items-center gap-2 text-xs opacity-70">
|
||||
<span>{message.timestamp.toLocaleTimeString('uk-UA')}</span>
|
||||
{message.tokens && (
|
||||
<span className="flex items-center gap-1">
|
||||
• {message.tokens} tokens
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{message.role === 'user' && (
|
||||
<div className="w-8 h-8 bg-gradient-to-br from-blue-500 to-cyan-500 rounded-lg flex items-center justify-center flex-shrink-0">
|
||||
<span className="text-white text-sm">👤</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
|
||||
{isLoading && (
|
||||
<div className="flex gap-3 justify-start">
|
||||
<div className="w-8 h-8 bg-gradient-to-br from-purple-500 to-pink-500 rounded-lg flex items-center justify-center flex-shrink-0">
|
||||
<Bot className="w-5 h-5 text-white" />
|
||||
</div>
|
||||
<div className="bg-gray-50 px-4 py-3 rounded-2xl">
|
||||
<Loader2 className="w-5 h-5 animate-spin text-purple-500" />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div ref={messagesEndRef} />
|
||||
</div>
|
||||
|
||||
{/* Input Area */}
|
||||
<div className="border-t border-gray-100 p-4 bg-gray-50">
|
||||
{error && (
|
||||
<div className="mb-3 px-4 py-2 bg-red-50 border border-red-200 text-red-700 rounded-lg text-sm">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex gap-2">
|
||||
<input
|
||||
type="text"
|
||||
value={input}
|
||||
onChange={(e) => setInput(e.target.value)}
|
||||
onKeyPress={handleKeyPress}
|
||||
placeholder="Напишіть повідомлення..."
|
||||
className="flex-1 px-4 py-3 bg-white border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent"
|
||||
disabled={isLoading}
|
||||
/>
|
||||
<button
|
||||
onClick={sendMessage}
|
||||
disabled={!input.trim() || isLoading}
|
||||
className="px-6 py-3 bg-gradient-to-br from-purple-500 to-pink-500 text-white rounded-xl hover:from-purple-600 hover:to-pink-600 disabled:opacity-50 disabled:cursor-not-allowed transition-all flex items-center gap-2 shadow-lg hover:shadow-xl"
|
||||
>
|
||||
{isLoading ? (
|
||||
<Loader2 className="w-5 h-5 animate-spin" />
|
||||
) : (
|
||||
<Send className="w-5 h-5" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
<div className="mt-2 text-xs text-gray-500 text-center">
|
||||
Sofia працює з локальним Ollama • qwen2.5-coder:32b
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -12,3 +12,12 @@ export { knowledgeBaseService, KnowledgeBaseService } from './knowledgeBaseServi
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -221,3 +221,12 @@ export const knowledgeBaseService = new KnowledgeBaseService();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -143,3 +143,12 @@ export const voiceService = new VoiceService();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -106,3 +106,12 @@ export const webSearchService = new WebSearchService();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -100,3 +100,12 @@ export function useActor() {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user