guardia-messenger/lib/ollama.ts

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
}
}