fix oauth package name
This commit is contained in:
234
oauth/src/hooks/useAskAI.js
Normal file
234
oauth/src/hooks/useAskAI.js
Normal file
@@ -0,0 +1,234 @@
|
||||
import { useState } from 'react'
|
||||
import { atproto, collections } from '../api/atproto.js'
|
||||
import { env } from '../config/env.js'
|
||||
import { logger } from '../utils/logger.js'
|
||||
import { getErrorMessage, logError } from '../utils/errorHandler.js'
|
||||
|
||||
export function useAskAI(adminData, userProfile, agent) {
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [error, setError] = useState(null)
|
||||
const [chatHistory, setChatHistory] = useState([])
|
||||
|
||||
// ask-AIサーバーのURL(環境変数から取得、フォールバック付き)
|
||||
const askAIUrl = import.meta.env.VITE_ASK_AI_URL || 'http://localhost:3000/ask'
|
||||
|
||||
const askQuestion = async (question) => {
|
||||
if (!question.trim()) return
|
||||
|
||||
setLoading(true)
|
||||
setError(null)
|
||||
|
||||
try {
|
||||
logger.log('Sending question to ask-AI:', question)
|
||||
|
||||
// ask-AIサーバーにリクエスト送信
|
||||
const response = await fetch(askAIUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
question: question.trim(),
|
||||
context: {
|
||||
url: window.location.href,
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`ask-AI server error: ${response.status}`)
|
||||
}
|
||||
|
||||
const aiResponse = await response.json()
|
||||
logger.log('Received AI response:', aiResponse)
|
||||
|
||||
// AI回答をチャット履歴に追加
|
||||
const chatEntry = {
|
||||
id: `chat-${Date.now()}`,
|
||||
question: question.trim(),
|
||||
answer: aiResponse.answer || 'エラーが発生しました',
|
||||
timestamp: new Date().toISOString(),
|
||||
user: userProfile ? {
|
||||
did: userProfile.did,
|
||||
handle: userProfile.handle,
|
||||
displayName: userProfile.displayName,
|
||||
avatar: userProfile.avatar
|
||||
} : null
|
||||
}
|
||||
|
||||
setChatHistory(prev => [...prev, chatEntry])
|
||||
|
||||
// atprotoにレコードを保存
|
||||
await saveChatRecord(chatEntry, aiResponse)
|
||||
|
||||
// Dispatch event for blog communication
|
||||
window.dispatchEvent(new CustomEvent('aiResponseReceived', {
|
||||
detail: {
|
||||
question: chatEntry.question,
|
||||
answer: chatEntry.answer,
|
||||
timestamp: chatEntry.timestamp,
|
||||
aiProfile: adminData?.profile ? {
|
||||
did: adminData.did,
|
||||
handle: adminData.profile.handle,
|
||||
displayName: adminData.profile.displayName,
|
||||
avatar: adminData.profile.avatar
|
||||
} : null
|
||||
}
|
||||
}))
|
||||
|
||||
return aiResponse
|
||||
|
||||
} catch (err) {
|
||||
logError(err, 'useAskAI.askQuestion')
|
||||
setError(getErrorMessage(err))
|
||||
throw err
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const saveChatRecord = async (chatEntry, aiResponse) => {
|
||||
if (!agent || !adminData?.did) {
|
||||
logger.warn('Cannot save chat record: missing agent or admin data')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const currentUrl = window.location.href
|
||||
const timestamp = chatEntry.timestamp
|
||||
const baseRkey = `${new Date(timestamp).toISOString().replace(/[:.]/g, '-').slice(0, -5)}Z`
|
||||
|
||||
// Post metadata (共通)
|
||||
const postMetadata = {
|
||||
url: currentUrl,
|
||||
date: timestamp,
|
||||
slug: new URL(currentUrl).pathname.split('/').pop()?.replace(/\.html$/, '') || '',
|
||||
tags: [],
|
||||
title: document.title || 'AI Chat',
|
||||
language: 'ja'
|
||||
}
|
||||
|
||||
// Question record (ユーザーの質問)
|
||||
const questionRecord = {
|
||||
repo: adminData.did,
|
||||
collection: `${env.collection}.chat`,
|
||||
rkey: baseRkey,
|
||||
record: {
|
||||
$type: `${env.collection}.chat`,
|
||||
post: postMetadata,
|
||||
text: chatEntry.question,
|
||||
type: 'question',
|
||||
author: chatEntry.user ? {
|
||||
did: chatEntry.user.did,
|
||||
handle: chatEntry.user.handle,
|
||||
displayName: chatEntry.user.displayName,
|
||||
avatar: chatEntry.user.avatar
|
||||
} : {
|
||||
did: 'unknown',
|
||||
handle: 'user',
|
||||
displayName: 'User',
|
||||
avatar: null
|
||||
},
|
||||
createdAt: timestamp
|
||||
}
|
||||
}
|
||||
|
||||
// Answer record (AIの回答)
|
||||
const answerRecord = {
|
||||
repo: adminData.did,
|
||||
collection: `${env.collection}.chat`,
|
||||
rkey: `${baseRkey}-answer`,
|
||||
record: {
|
||||
$type: `${env.collection}.chat`,
|
||||
post: postMetadata,
|
||||
text: chatEntry.answer,
|
||||
type: 'answer',
|
||||
author: {
|
||||
did: adminData.did,
|
||||
handle: adminData.profile?.handle || 'ai',
|
||||
displayName: adminData.profile?.displayName || 'ai',
|
||||
avatar: adminData.profile?.avatar || null
|
||||
},
|
||||
createdAt: timestamp
|
||||
}
|
||||
}
|
||||
|
||||
logger.log('Saving question record to atproto:', questionRecord)
|
||||
await atproto.putRecord(null, questionRecord, agent)
|
||||
|
||||
logger.log('Saving answer record to atproto:', answerRecord)
|
||||
await atproto.putRecord(null, answerRecord, agent)
|
||||
|
||||
// キャッシュを無効化
|
||||
collections.invalidateCache(env.collection)
|
||||
|
||||
logger.log('Chat records saved successfully')
|
||||
|
||||
} catch (err) {
|
||||
logError(err, 'useAskAI.saveChatRecord')
|
||||
// 保存エラーは致命的ではないので、UIエラーにはしない
|
||||
}
|
||||
}
|
||||
|
||||
const clearChatHistory = () => {
|
||||
setChatHistory([])
|
||||
setError(null)
|
||||
}
|
||||
|
||||
const loadChatHistory = async () => {
|
||||
if (!adminData?.did) return
|
||||
|
||||
try {
|
||||
const records = await collections.getChat(
|
||||
adminData.apiConfig.pds,
|
||||
adminData.did,
|
||||
env.collection
|
||||
)
|
||||
|
||||
// Group records by timestamp and create Q&A pairs
|
||||
const recordGroups = {}
|
||||
|
||||
records.forEach(record => {
|
||||
const timestamp = record.value.createdAt
|
||||
const baseKey = timestamp.replace('-answer', '')
|
||||
|
||||
if (!recordGroups[baseKey]) {
|
||||
recordGroups[baseKey] = {}
|
||||
}
|
||||
|
||||
if (record.value.type === 'question') {
|
||||
recordGroups[baseKey].question = record.value.text
|
||||
recordGroups[baseKey].user = record.value.author
|
||||
recordGroups[baseKey].timestamp = timestamp
|
||||
recordGroups[baseKey].id = record.uri
|
||||
} else if (record.value.type === 'answer') {
|
||||
recordGroups[baseKey].answer = record.value.text
|
||||
recordGroups[baseKey].timestamp = timestamp
|
||||
}
|
||||
})
|
||||
|
||||
// Convert to history format, only include complete Q&A pairs
|
||||
const history = Object.values(recordGroups)
|
||||
.filter(group => group.question && group.answer)
|
||||
.sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp))
|
||||
.slice(-10) // 最新10件のみ
|
||||
|
||||
setChatHistory(history)
|
||||
logger.log('Chat history loaded:', history.length, 'entries')
|
||||
|
||||
} catch (err) {
|
||||
logError(err, 'useAskAI.loadChatHistory')
|
||||
// 履歴読み込みエラーは致命的ではない
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
askQuestion,
|
||||
loading,
|
||||
error,
|
||||
chatHistory,
|
||||
clearChatHistory,
|
||||
loadChatHistory
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user