89 lines
2.7 KiB
TypeScript
89 lines
2.7 KiB
TypeScript
/**
|
|
* GUARDiA Messenger — Ollama 온프레미스 AI 클라이언트
|
|
*
|
|
* 보안 원칙 (불변):
|
|
* - 외부 AI API 절대 사용 금지. localhost:11434 (온프레미스 Ollama)만 사용.
|
|
* - 서버 자격증명(IP/SSH 계정/비밀번호)을 프롬프트에 포함 금지.
|
|
* - Ollama 미가동/오프라인 시 안전하게 빈 값/폴백 반환 (앱 크래시 방지).
|
|
*/
|
|
|
|
// 외부 AI API 절대 사용 금지 — localhost:11434만 사용
|
|
const OLLAMA_BASE = 'http://localhost:11434'
|
|
|
|
// 기본 모델 (온프레미스 sLLM)
|
|
export const DEFAULT_TEXT_MODEL = 'llama3'
|
|
export const DEFAULT_VISION_MODEL = 'llava'
|
|
|
|
interface OllamaResponse {
|
|
response?: string
|
|
done?: boolean
|
|
}
|
|
|
|
/** 단순 텍스트 생성. 실패 시 빈 문자열 반환. */
|
|
export async function generate(model: string, prompt: string): Promise<string> {
|
|
try {
|
|
const res = await fetch(`${OLLAMA_BASE}/api/generate`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ model, prompt, stream: false }),
|
|
})
|
|
const data: OllamaResponse = await res.json()
|
|
return data.response ?? ''
|
|
} catch {
|
|
return '' // 오프라인/Ollama 미가동 시 빈 문자열 반환
|
|
}
|
|
}
|
|
|
|
/**
|
|
* JSON 응답 생성. sLLM 응답에서 JSON 블록을 추출/파싱.
|
|
* 파싱 실패 시 fallback 반환.
|
|
*/
|
|
export async function generateJSON<T>(model: string, prompt: string, fallback: T): Promise<T> {
|
|
const raw = await generate(model, prompt)
|
|
if (!raw) return fallback
|
|
try {
|
|
return JSON.parse(raw) as T
|
|
} catch {
|
|
// 모델이 부연 설명과 함께 JSON을 출력한 경우 JSON 블록만 추출 시도
|
|
const match = raw.match(/[\[{][\s\S]*[\]}]/)
|
|
if (match) {
|
|
try {
|
|
return JSON.parse(match[0]) as T
|
|
} catch {
|
|
return fallback
|
|
}
|
|
}
|
|
return fallback
|
|
}
|
|
}
|
|
|
|
/** 이미지 + 텍스트 멀티모달 생성 (llava). imageBase64는 data URI 접두어 없는 순수 base64. */
|
|
export async function generateWithImage(
|
|
model: string,
|
|
prompt: string,
|
|
imageBase64: string,
|
|
): Promise<string> {
|
|
try {
|
|
const clean = imageBase64.replace(/^data:image\/\w+;base64,/, '')
|
|
const res = await fetch(`${OLLAMA_BASE}/api/generate`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ model, prompt, images: [clean], stream: false }),
|
|
})
|
|
const data: OllamaResponse = await res.json()
|
|
return data.response ?? ''
|
|
} catch {
|
|
return ''
|
|
}
|
|
}
|
|
|
|
/** Ollama 서버 가동 여부 확인. */
|
|
export async function isOllamaAvailable(): Promise<boolean> {
|
|
try {
|
|
const res = await fetch(`${OLLAMA_BASE}/api/tags`, { method: 'GET' })
|
|
return res.ok
|
|
} catch {
|
|
return false
|
|
}
|
|
}
|