80 lines
2.8 KiB
TypeScript
80 lines
2.8 KiB
TypeScript
/**
|
|
* NextActions (#20) — 다음 명령 3개 제안
|
|
*
|
|
* 현재 컨텍스트(SR 상태/서버 상태 등)를 Ollama에 전달 → 다음 행동 3가지 JSON 배열.
|
|
* 버튼 3개로 표시, 탭하면 onSelect 콜백 호출.
|
|
*
|
|
* 보안: 컨텍스트에 자격증명/IP 포함 금지 (호출 측 책임). 온프레미스 Ollama만 사용.
|
|
*/
|
|
import { useState, useEffect } from 'react'
|
|
import { View, Text, Pressable, StyleSheet, ActivityIndicator } from 'react-native'
|
|
import { COLORS } from '../constants/Config'
|
|
import { generateJSON, DEFAULT_TEXT_MODEL } from '../lib/ollama'
|
|
|
|
interface Props {
|
|
context: string
|
|
onSelect: (action: string) => void
|
|
}
|
|
|
|
const FALLBACK: string[] = ['KB 조회', '담당자에게 연락', '에스컬레이션']
|
|
|
|
export function NextActions({ context, onSelect }: Props) {
|
|
const [actions, setActions] = useState<string[]>([])
|
|
const [loading, setLoading] = useState(false)
|
|
|
|
useEffect(() => {
|
|
let alive = true
|
|
;(async () => {
|
|
if (!context?.trim()) {
|
|
setActions(FALLBACK)
|
|
return
|
|
}
|
|
setLoading(true)
|
|
const prompt =
|
|
`당신은 IT 운영 어시스턴트입니다. 운영자가 다음 상황에 있습니다: "${context}". ` +
|
|
`다음으로 취할 행동 3가지를 한국어 짧은 문구로, 설명 없이 JSON 배열만 출력하세요. ` +
|
|
`예: ["에스컬레이션","KB 조회","담당자 연락"]`
|
|
const result = await generateJSON<string[]>(DEFAULT_TEXT_MODEL, prompt, FALLBACK)
|
|
if (!alive) return
|
|
const clean = Array.isArray(result) ? result.filter(x => typeof x === 'string').slice(0, 3) : FALLBACK
|
|
setActions(clean.length ? clean : FALLBACK)
|
|
setLoading(false)
|
|
})()
|
|
return () => {
|
|
alive = false
|
|
}
|
|
}, [context])
|
|
|
|
return (
|
|
<View style={S.wrap}>
|
|
<Text style={S.title}>⚡ 다음 추천 작업</Text>
|
|
{loading ? (
|
|
<ActivityIndicator color={COLORS.accent} style={{ marginVertical: 8 }} />
|
|
) : (
|
|
<View style={S.row}>
|
|
{actions.map((a, i) => (
|
|
<Pressable key={i} style={S.chip} onPress={() => onSelect(a)}>
|
|
<Text style={S.chipText} numberOfLines={2}>
|
|
{a}
|
|
</Text>
|
|
</Pressable>
|
|
))}
|
|
</View>
|
|
)}
|
|
</View>
|
|
)
|
|
}
|
|
|
|
export default NextActions
|
|
|
|
const S = StyleSheet.create({
|
|
wrap: { backgroundColor: COLORS.card, borderRadius: 14, padding: 14, borderWidth: 1, borderColor: COLORS.border },
|
|
title: { fontSize: 13, fontWeight: '700', color: COLORS.text, marginBottom: 8 },
|
|
row: { flexDirection: 'row', gap: 8 },
|
|
chip: {
|
|
flex: 1, backgroundColor: COLORS.light, borderRadius: 10, paddingVertical: 12,
|
|
paddingHorizontal: 8, alignItems: 'center', justifyContent: 'center',
|
|
},
|
|
chipText: { fontSize: 12, fontWeight: '600', color: COLORS.blue, textAlign: 'center' },
|
|
})
|