feat: AI 서비스 및 AI 어시스턴트 전용 페이지 추가
- ai-service: Ollama 기반 AI 서비스 (분류, 시맨틱 검색, RAG Q&A, 패턴 분석) - AI 어시스턴트 페이지: 채팅형 Q&A, 시맨틱 검색, 패턴 분석, 분류 테스트 - 권한 시스템에 ai_assistant 페이지 등록 (기본 비활성) - 기존 페이지에 AI 기능 통합 (대시보드, 수신함, 관리함) - docker-compose, gateway, nginx 설정 업데이트 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -308,6 +308,161 @@ function checkPageAccess(pageName) {
|
||||
return user;
|
||||
}
|
||||
|
||||
// AI API
|
||||
const AiAPI = {
|
||||
getSimilarIssues: async (issueId, limit = 5) => {
|
||||
try {
|
||||
const res = await fetch(`/ai-api/similar/${issueId}?n_results=${limit}`, {
|
||||
headers: { 'Authorization': `Bearer ${TokenManager.getToken()}` }
|
||||
});
|
||||
if (!res.ok) return { available: false, results: [] };
|
||||
return await res.json();
|
||||
} catch (e) {
|
||||
console.warn('AI 유사 검색 실패:', e);
|
||||
return { available: false, results: [] };
|
||||
}
|
||||
},
|
||||
searchSimilar: async (query, limit = 5, filters = {}) => {
|
||||
try {
|
||||
const res = await fetch('/ai-api/similar/search', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${TokenManager.getToken()}`
|
||||
},
|
||||
body: JSON.stringify({ query, n_results: limit, ...filters })
|
||||
});
|
||||
if (!res.ok) return { available: false, results: [] };
|
||||
return await res.json();
|
||||
} catch (e) {
|
||||
console.warn('AI 검색 실패:', e);
|
||||
return { available: false, results: [] };
|
||||
}
|
||||
},
|
||||
classifyIssue: async (description, detailNotes = '') => {
|
||||
try {
|
||||
const res = await fetch('/ai-api/classify', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${TokenManager.getToken()}`
|
||||
},
|
||||
body: JSON.stringify({ description, detail_notes: detailNotes })
|
||||
});
|
||||
if (!res.ok) return { available: false };
|
||||
return await res.json();
|
||||
} catch (e) {
|
||||
console.warn('AI 분류 실패:', e);
|
||||
return { available: false };
|
||||
}
|
||||
},
|
||||
generateDailyReport: async (date, projectId) => {
|
||||
try {
|
||||
const res = await fetch('/ai-api/report/daily', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${TokenManager.getToken()}`
|
||||
},
|
||||
body: JSON.stringify({ date, project_id: projectId })
|
||||
});
|
||||
if (!res.ok) return { available: false };
|
||||
return await res.json();
|
||||
} catch (e) {
|
||||
console.warn('AI 보고서 생성 실패:', e);
|
||||
return { available: false };
|
||||
}
|
||||
},
|
||||
syncEmbeddings: async () => {
|
||||
try {
|
||||
const res = await fetch('/ai-api/embeddings/sync', {
|
||||
method: 'POST',
|
||||
headers: { 'Authorization': `Bearer ${TokenManager.getToken()}` }
|
||||
});
|
||||
if (!res.ok) return { status: 'error' };
|
||||
return await res.json();
|
||||
} catch (e) {
|
||||
return { status: 'error' };
|
||||
}
|
||||
},
|
||||
checkHealth: async () => {
|
||||
try {
|
||||
const res = await fetch('/ai-api/health');
|
||||
return await res.json();
|
||||
} catch (e) {
|
||||
return { status: 'disconnected' };
|
||||
}
|
||||
},
|
||||
// RAG: 해결방안 제안
|
||||
suggestSolution: async (issueId) => {
|
||||
try {
|
||||
const res = await fetch(`/ai-api/rag/suggest-solution/${issueId}`, {
|
||||
method: 'POST',
|
||||
headers: { 'Authorization': `Bearer ${TokenManager.getToken()}` }
|
||||
});
|
||||
if (!res.ok) return { available: false };
|
||||
return await res.json();
|
||||
} catch (e) {
|
||||
console.warn('AI 해결방안 제안 실패:', e);
|
||||
return { available: false };
|
||||
}
|
||||
},
|
||||
// RAG: 자연어 질의
|
||||
askQuestion: async (question, projectId = null) => {
|
||||
try {
|
||||
const res = await fetch('/ai-api/rag/ask', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${TokenManager.getToken()}`
|
||||
},
|
||||
body: JSON.stringify({ question, project_id: projectId })
|
||||
});
|
||||
if (!res.ok) return { available: false };
|
||||
return await res.json();
|
||||
} catch (e) {
|
||||
console.warn('AI 질의 실패:', e);
|
||||
return { available: false };
|
||||
}
|
||||
},
|
||||
// RAG: 패턴 분석
|
||||
analyzePattern: async (description) => {
|
||||
try {
|
||||
const res = await fetch('/ai-api/rag/pattern', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${TokenManager.getToken()}`
|
||||
},
|
||||
body: JSON.stringify({ description })
|
||||
});
|
||||
if (!res.ok) return { available: false };
|
||||
return await res.json();
|
||||
} catch (e) {
|
||||
console.warn('AI 패턴 분석 실패:', e);
|
||||
return { available: false };
|
||||
}
|
||||
},
|
||||
// RAG: 강화 분류 (과거 사례 참고)
|
||||
classifyWithRAG: async (description, detailNotes = '') => {
|
||||
try {
|
||||
const res = await fetch('/ai-api/rag/classify', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${TokenManager.getToken()}`
|
||||
},
|
||||
body: JSON.stringify({ description, detail_notes: detailNotes })
|
||||
});
|
||||
if (!res.ok) return { available: false };
|
||||
return await res.json();
|
||||
} catch (e) {
|
||||
console.warn('AI RAG 분류 실패:', e);
|
||||
return { available: false };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 프로젝트 API
|
||||
const ProjectsAPI = {
|
||||
getAll: (activeOnly = false) => {
|
||||
|
||||
Reference in New Issue
Block a user