112 lines
5.6 KiB
TypeScript
112 lines
5.6 KiB
TypeScript
import React, { useState, useCallback } from 'react';
|
|
import { View, Text, TextInput, ScrollView, TouchableOpacity, StyleSheet, ActivityIndicator } from 'react-native';
|
|
import { ITSM_BASE } from '../../services/api';
|
|
|
|
const AGENTS = [
|
|
{ id: 'sr-manager', name: 'SR 관리자', icon: '📋', desc: 'SR 접수·분류·배정 자동화' },
|
|
{ id: 'incident-responder', name: '인시던트 대응', icon: '🚨', desc: '장애 감지·RCA·복구' },
|
|
{ id: 'deploy-engineer', name: '배포 엔지니어', icon: '🚀', desc: 'SSH 배포·롤백·헬스체크' },
|
|
{ id: 'ai-analyst', name: 'AI 분석가', icon: '🤖', desc: '이상탐지·예측·인사이트' },
|
|
{ id: 'csap-auditor', name: 'CSAP 감사관', icon: '🛡️', desc: 'CSAP 준수율 자동 점검' },
|
|
];
|
|
|
|
interface Message { role: 'user' | 'agent'; content: string; agent?: string; ts: string }
|
|
|
|
export default function AIAgentScreen() {
|
|
const [messages, setMessages] = useState<Message[]>([]);
|
|
const [input, setInput] = useState('');
|
|
const [selectedAgent, setSelectedAgent] = useState(AGENTS[0]);
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
const sendMessage = useCallback(async () => {
|
|
if (!input.trim()) return;
|
|
const userMsg: Message = { role: 'user', content: input, ts: new Date().toISOString() };
|
|
setMessages(prev => [...prev, userMsg]);
|
|
setInput('');
|
|
setLoading(true);
|
|
try {
|
|
const r = await fetch(`${ITSM_BASE}/api/agent-collab/rooms`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ topic: input, agents: [selectedAgent.id] }),
|
|
});
|
|
if (r.ok) {
|
|
const room = await r.json();
|
|
const opinion = await fetch(`${ITSM_BASE}/api/agent-collab/rooms/${room.id}/ai-opinion`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ agent_id: selectedAgent.id }),
|
|
});
|
|
if (opinion.ok) {
|
|
const data = await opinion.json();
|
|
setMessages(prev => [...prev, {
|
|
role: 'agent', content: data.opinion || data.message || '처리 완료',
|
|
agent: selectedAgent.name, ts: new Date().toISOString(),
|
|
}]);
|
|
}
|
|
}
|
|
} catch {
|
|
setMessages(prev => [...prev, { role: 'agent', content: '[Ollama 처리 중...]', agent: selectedAgent.name, ts: new Date().toISOString() }]);
|
|
} finally { setLoading(false); }
|
|
}, [input, selectedAgent]);
|
|
|
|
return (
|
|
<View style={s.container}>
|
|
<Text style={s.title}>AI 에이전트 채팅</Text>
|
|
<ScrollView horizontal showsHorizontalScrollIndicator={false} style={s.agentBar}>
|
|
{AGENTS.map(a => (
|
|
<TouchableOpacity key={a.id} style={[s.agentChip, selectedAgent.id === a.id && s.agentActive]}
|
|
onPress={() => setSelectedAgent(a)}>
|
|
<Text style={s.agentIcon}>{a.icon}</Text>
|
|
<Text style={[s.agentName, selectedAgent.id === a.id && s.agentNameActive]}>{a.name}</Text>
|
|
</TouchableOpacity>
|
|
))}
|
|
</ScrollView>
|
|
<View style={s.agentDesc}><Text style={s.agentDescText}>{selectedAgent.icon} {selectedAgent.desc}</Text></View>
|
|
<ScrollView style={s.chat}>
|
|
{messages.map((m, i) => (
|
|
<View key={i} style={[s.bubble, m.role === 'user' ? s.userBubble : s.agentBubble]}>
|
|
{m.role === 'agent' && <Text style={s.agentLabel}>{m.agent}</Text>}
|
|
<Text style={s.bubbleText}>{m.content}</Text>
|
|
<Text style={s.ts}>{new Date(m.ts).toLocaleTimeString()}</Text>
|
|
</View>
|
|
))}
|
|
{loading && <ActivityIndicator color="#00A0C8" style={{ margin: 12 }} />}
|
|
</ScrollView>
|
|
<View style={s.inputRow}>
|
|
<TextInput style={s.input} value={input} onChangeText={setInput}
|
|
placeholder="에이전트에게 질문하세요..." placeholderTextColor="#888"
|
|
onSubmitEditing={sendMessage} />
|
|
<TouchableOpacity style={s.sendBtn} onPress={sendMessage}>
|
|
<Text style={s.sendText}>전송</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
const s = StyleSheet.create({
|
|
container: { flex: 1, backgroundColor: '#0A0E1A' },
|
|
title: { color: '#fff', fontSize: 18, fontWeight: '700', padding: 16, paddingBottom: 8 },
|
|
agentBar: { paddingHorizontal: 12, paddingVertical: 8, maxHeight: 80 },
|
|
agentChip: { alignItems: 'center', marginRight: 12, paddingHorizontal: 12, paddingVertical: 8,
|
|
borderRadius: 20, borderWidth: 1, borderColor: '#333', backgroundColor: '#1A1F2E' },
|
|
agentActive: { borderColor: '#00A0C8', backgroundColor: '#003366' },
|
|
agentIcon: { fontSize: 18 },
|
|
agentName: { color: '#aaa', fontSize: 11, marginTop: 2 },
|
|
agentNameActive: { color: '#00A0C8' },
|
|
agentDesc: { backgroundColor: '#1A1F2E', marginHorizontal: 12, marginBottom: 4, padding: 8, borderRadius: 8 },
|
|
agentDescText: { color: '#aaa', fontSize: 12 },
|
|
chat: { flex: 1, padding: 12 },
|
|
bubble: { maxWidth: '80%', marginBottom: 12, padding: 10, borderRadius: 12 },
|
|
userBubble: { alignSelf: 'flex-end', backgroundColor: '#003366' },
|
|
agentBubble: { alignSelf: 'flex-start', backgroundColor: '#1A1F2E' },
|
|
agentLabel: { color: '#00A0C8', fontSize: 11, fontWeight: '600', marginBottom: 4 },
|
|
bubbleText: { color: '#fff', fontSize: 14, lineHeight: 20 },
|
|
ts: { color: '#555', fontSize: 10, marginTop: 4, textAlign: 'right' },
|
|
inputRow: { flexDirection: 'row', padding: 12, borderTopWidth: 1, borderTopColor: '#222' },
|
|
input: { flex: 1, backgroundColor: '#1A1F2E', color: '#fff', borderRadius: 20, paddingHorizontal: 16, marginRight: 8 },
|
|
sendBtn: { backgroundColor: '#00A0C8', borderRadius: 20, paddingHorizontal: 16, justifyContent: 'center' },
|
|
sendText: { color: '#fff', fontWeight: '700' },
|
|
});
|