-
-
Notifications
You must be signed in to change notification settings - Fork 143
会話履歴更新時にSupabaseにデータを保存 & ローカルLLM修正 #252
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
Walkthroughこのプルリクエストでは、ローカルLLM機能に関連する新しいエラーメッセージが複数の言語の翻訳ファイルに追加されました。具体的には、英語、日本語、韓国語、中国語の各ファイルに5つの新しいエラーキーが追加され、エラーメッセージの詳細が強化されました。また、 Changes
Possibly related PRs
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
🧹 Outside diff range and nitpick comments (3)
src/features/chat/localLLMChat.ts (1)
6-10: グローバルな言語設定の変更を避け、特定の言語でメッセージを取得してくださいエラー処理中に
i18next.changeLanguageを使用すると、アプリ全体の言語設定が変更されてしまいます。代わりに、i18next.tに言語オプションを指定して、グローバルな言語設定を変更せずに翻訳を取得することをお勧めします。以下のように修正できます:
function handleApiError(errorCode: string): string { const languageCode = settingsStore.getState().selectLanguage - i18next.changeLanguage(languageCode) - return i18next.t(`Errors.${errorCode || 'LocalLLMError'}`) + return i18next.t(`Errors.${errorCode || 'LocalLLMError'}`, { lng: languageCode }) }locales/en/translation.json (1)
147-152: 全言語間での翻訳の一貫性が優れています以下の点で高品質な翻訳が実現されています:
- 技術用語(「Local LLM」「ローカルLLM」「로컬 LLM」)の一貫した使用
- エラーメッセージの構造的な統一性
- 各言語の言語特性に応じた適切な表現の選択
今後のローカライゼーション拡張時にも、この翻訳スタイルを参考にすることを推奨します。
Also applies to: 147-151, 147-151
src/components/settings/modelProvider.tsx (1)
Line range hint
1-1000: コンポーネントの分割とリファクタリングを推奨現在のコンポーネントは非常に大きく、保守性に課題があります。以下の改善を提案します:
- 各AIサービスを個別のコンポーネントに分割
- 共通の入力フィールドをコンポーネント化
- 設定の検証ロジックを独立したユーティリティに分離
以下のような構造を提案します:
// components/settings/providers/LocalLLMProvider.tsx export const LocalLLMProvider = () => { // LocalLLM固有の実装 }; // components/settings/providers/OpenAIProvider.tsx export const OpenAIProvider = () => { // OpenAI固有の実装 }; // components/settings/common/APIKeyInput.tsx export const APIKeyInput = ({ value, onChange, label, linkUrl }: APIKeyInputProps) => { // 共通のAPI入力UI };このリファクタリングにより:
- コードの可読性が向上
- テストが容易になる
- 機能追加や変更が簡単になる
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (10)
locales/en/translation.json(1 hunks)locales/ja/translation.json(1 hunks)locales/ko/translation.json(1 hunks)locales/zh/translation.json(1 hunks)package.json(1 hunks)src/components/settings/modelProvider.tsx(1 hunks)src/features/chat/localLLMChat.ts(1 hunks)src/pages/api/local-llm.ts(1 hunks)src/pages/api/save-chat-log.ts(3 hunks)tsconfig.json(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- package.json
🔇 Additional comments (6)
tsconfig.json (2)
20-24: Next.jsのプラグイン設定の追加を確認しました
"plugins" セクションに Next.js のプラグインを追加しており、適切な設定です。
26-26: 型定義のパスを include に追加したことを確認しました
.next/types/**/*.ts を include に追加しており、コンパイル時に Next.js の型定義が正しく認識されるようになります。
locales/zh/translation.json (1)
147-151: エラーメッセージの追加を確認しました
新しいエラーメッセージキーが適切に追加されています。これにより、ローカルLLMに関連するエラーがユーザーに明確に伝えられます。
locales/ja/translation.json (1)
148-152: エラーメッセージの翻訳が適切です
エラーメッセージの日本語訳が明確で、技術用語の一貫性が保たれています。
locales/ko/translation.json (1)
147-151: 日本語版との一貫性が確保されています
韓国語版のエラーメッセージが日本語版と同様の構造を保ち、技術用語の翻訳も適切に対応しています。
locales/en/translation.json (1)
147-151: 英語版と日本語版で適切な言語適応がされています
英語版は簡潔な表現を使用しつつ、日本語版では必要な説明的な要素が追加されており、各言語の特性に合わせた適切な翻訳となっています。
| const trimmedChunks = accumulatedChunks.trimStart() | ||
| const data = JSON.parse(trimmedChunks.slice(6)) | ||
|
|
||
| if (data.choices && data.choices.length > 0) { | ||
| const content = data.choices[0].delta.content | ||
| controller.enqueue(content) | ||
| accumulatedChunks = '' | ||
| } | ||
| } catch (error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
JSONパース時のデータ処理をより堅牢にしてください
trimmedChunks.slice(6) で先頭6文字を削除していますが、固定の文字数を切り取る方法はデータ形式の変更に弱く、エラーを引き起こす可能性があります。データの先頭に余分な文字が含まれる理由を確認し、必要であれば正規表現やプロトコルに基づいて不要な部分を安全に除去する方法を検討してください。
修正の提案:
- データの先頭にある不要なプレフィックスがある場合、そのプレフィックスを明示的に検出して削除する。
- 可能であれば、サーバー側のレスポンス形式を調整し、クライアント側での追加処理を不要にする。
| const lastMessage = processedMessages[processedMessages.length - 1] | ||
| const messageToSave = { | ||
| session_id: sessionId, | ||
| role: lastMessage.role, | ||
| content: Array.isArray(lastMessage.content) | ||
| ? JSON.stringify(lastMessage.content) | ||
| : lastMessage.content, | ||
| created_at: created_at, | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
メッセージが存在しない場合の処理を追加してください
processedMessages が空の場合、processedMessages[processedMessages.length - 1] は undefined となり、エラーが発生します。メッセージが存在するか確認し、適切なエラーハンドリングを行ってください。
以下のように修正できます:
const lastMessage = processedMessages[processedMessages.length - 1]
+ if (!lastMessage) {
+ throw new Error('メッセージが存在しません')
+ }
const messageToSave = {
session_id: sessionId,
role: lastMessage.role,
content: Array.isArray(lastMessage.content)
? JSON.stringify(lastMessage.content)
: lastMessage.content,
created_at: created_at,
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const lastMessage = processedMessages[processedMessages.length - 1] | |
| const messageToSave = { | |
| session_id: sessionId, | |
| role: lastMessage.role, | |
| content: Array.isArray(lastMessage.content) | |
| ? JSON.stringify(lastMessage.content) | |
| : lastMessage.content, | |
| created_at: created_at, | |
| } | |
| const lastMessage = processedMessages[processedMessages.length - 1] | |
| if (!lastMessage) { | |
| throw new Error('メッセージが存在しません') | |
| } | |
| const messageToSave = { | |
| session_id: sessionId, | |
| role: lastMessage.role, | |
| content: Array.isArray(lastMessage.content) | |
| ? JSON.stringify(lastMessage.content) | |
| : lastMessage.content, | |
| created_at: created_at, | |
| } |
| const { data: existingSession } = await supabase | ||
| .from('local_chat_sessions') | ||
| .select() | ||
| .eq('title', fileName) | ||
| .single() | ||
|
|
||
| let sessionId | ||
|
|
||
| if (existingSession) { | ||
| // 既存のセッションが見つかった場合 | ||
| sessionId = existingSession.id | ||
|
|
||
| // updated_at のみ更新 | ||
| await supabase | ||
| .from('local_chat_sessions') | ||
| .update({ updated_at: created_at }) | ||
| .eq('id', sessionId) | ||
| } else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Supabase操作のエラーチェックを追加してください
select および update 操作でエラーが発生した場合、現在のコードではエラーが検出されず、予期しない動作につながる可能性があります。各操作の結果から error オブジェクトを取得し、エラーチェックを行うことをお勧めします。
以下のように修正できます:
// 既存のセッションを検索
- const { data: existingSession } = await supabase
+ const { data: existingSession, error: selectError } = await supabase
.from('local_chat_sessions')
.select()
.eq('title', fileName)
.single()
+ if (selectError) throw selectError
...
// updated_at のみ更新
- await supabase
+ const { error: updateError } = await supabase
.from('local_chat_sessions')
.update({ updated_at: created_at })
.eq('id', sessionId)
+ if (updateError) throw updateError📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const { data: existingSession } = await supabase | |
| .from('local_chat_sessions') | |
| .select() | |
| .eq('title', fileName) | |
| .single() | |
| let sessionId | |
| if (existingSession) { | |
| // 既存のセッションが見つかった場合 | |
| sessionId = existingSession.id | |
| // updated_at のみ更新 | |
| await supabase | |
| .from('local_chat_sessions') | |
| .update({ updated_at: created_at }) | |
| .eq('id', sessionId) | |
| } else { | |
| const { data: existingSession, error: selectError } = await supabase | |
| .from('local_chat_sessions') | |
| .select() | |
| .eq('title', fileName) | |
| .single() | |
| if (selectError) throw selectError | |
| let sessionId | |
| if (existingSession) { | |
| // 既存のセッションが見つかった場合 | |
| sessionId = existingSession.id | |
| // updated_at のみ更新 | |
| const { error: updateError } = await supabase | |
| .from('local_chat_sessions') | |
| .update({ updated_at: created_at }) | |
| .eq('id', sessionId) | |
| if (updateError) throw updateError | |
| } else { |
| const response = await axios.post( | ||
| body.localLlmUrl.replace(/\/$/, ''), | ||
| { | ||
| model: body.model, | ||
| messages: body.messages, | ||
| stream: true, | ||
| }, | ||
| { | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| }, | ||
| responseType: 'stream', | ||
| } | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ユーザー提供のURLへのリクエストに対するセキュリティ対策を実施してください
ユーザーから提供された localLlmUrl に対してサーバー側でリクエストを行うと、SSRF(サーバーサイドリクエストフォージェリ)の脆弱性を生む可能性があります。localLlmUrl の値を適切にバリデーションし、許可されたドメインやIPアドレスに限定するなどのセキュリティ対策を講じてください。
修正の提案:
localLlmUrlを許可リストに基づいて検証し、信頼できるURLのみを許可する。URLオブジェクトを使用してパースし、プロトコルやホスト名をチェックする。
| <br /> | ||
| ex. LM Studio: http://localhost:1234/v1/chat/completions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
URLの検証とセキュリティ強化が必要です
ローカルLLMのURL入力において、以下の改善が推奨されます:
- URLの形式検証
- プロトコルの制限(http/httpsのみ許可)
- ローカルホストのアドレス検証
以下のような検証ロジックの追加を提案します:
+ const validateLocalLlmUrl = (url: string): boolean => {
+ try {
+ const parsedUrl = new URL(url);
+ return (
+ (parsedUrl.protocol === 'http:' || parsedUrl.protocol === 'https:') &&
+ (parsedUrl.hostname === 'localhost' || parsedUrl.hostname === '127.0.0.1')
+ );
+ } catch {
+ return false;
+ }
+ };
<input
className="text-ellipsis px-16 py-8 w-col-span-2 bg-surface1 hover:bg-surface1-hover rounded-8"
type="text"
placeholder="..."
value={localLlmUrl}
- onChange={(e) =>
- settingsStore.setState({ localLlmUrl: e.target.value })
- }
+ onChange={(e) => {
+ const newUrl = e.target.value;
+ if (newUrl === '' || validateLocalLlmUrl(newUrl)) {
+ settingsStore.setState({ localLlmUrl: newUrl });
+ }
+ }}
/>Committable suggestion skipped: line range outside the PR's diff.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Outside diff range and nitpick comments (2)
src/pages/api/save-chat-log.ts (2)
8-13: 環境変数の検証を強化することを推奨します環境変数の存在チェックは行われていますが、より堅牢な実装が可能です。
以下のような実装を推奨します:
-if (process.env.SUPABASE_URL && process.env.SUPABASE_SERVICE_ROLE_KEY) { +const supabaseUrl = process.env.SUPABASE_URL +const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY + +if (!supabaseUrl || !supabaseKey) { + console.warn('Supabase credentials are not configured. Chat logs will only be saved locally.') +} else { supabase = createClient( - process.env.SUPABASE_URL!, - process.env.SUPABASE_SERVICE_ROLE_KEY! + supabaseUrl, + supabaseKey ) }
60-61: TODOコメントの詳細化または削除を検討してください「標準化する」というTODOコメントが曖昧です。具体的な課題や目標を記載することで、将来の開発者がより理解しやすくなります。
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (1)
src/pages/api/save-chat-log.ts(3 hunks)
🔇 Additional comments (2)
src/pages/api/save-chat-log.ts (2)
63-80: Supabase操作のエラーチェックを追加してください
selectおよびupdate操作のエラーハンドリングが不足しています。
97-106:
メッセージのバリデーションを追加してください
メッセージの処理前に適切なバリデーションを行うことで、より安全な実装になります。
以下のような実装を推奨します:
+interface Message {
+ role: string;
+ content: string | Array<{type: string; image?: string; text?: string}>;
+}
+
+function validateMessage(message: Message): void {
+ if (!message.role) {
+ throw new Error('メッセージにroleが含まれていません')
+ }
+ if (!message.content) {
+ throw new Error('メッセージにcontentが含まれていません')
+ }
+}
const lastMessage = processedMessages[processedMessages.length - 1]
+if (!lastMessage) {
+ throw new Error('メッセージが存在しません')
+}
+validateMessage(lastMessage)Likely invalid or redundant comment.
|
|
||
| try { | ||
| const { messages, isNewFile } = req.body | ||
| const created_at = new Date().toISOString() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ファイル操作のエラーハンドリングを追加してください
タイムスタンプの処理は適切ですが、ファイル操作に関するエラーハンドリングが不足しています。
以下のような実装を推奨します:
-const created_at = new Date().toISOString()
+let created_at: string
+try {
+ created_at = new Date().toISOString()
+} catch (error) {
+ console.error('タイムスタンプの生成に失敗しました:', error)
+ throw new Error('タイムスタンプの生成に失敗しました')
+}
// ...
-const fileName = isNewFile
- ? `log_${created_at.replace(/[:.]/g, '-')}.json`
- : getLatestLogFile(logsDir)
+let fileName: string
+try {
+ fileName = isNewFile
+ ? `log_${created_at.replace(/[:.]/g, '-')}.json`
+ : getLatestLogFile(logsDir)
+} catch (error) {
+ console.error('ファイル名の生成に失敗しました:', error)
+ throw new Error('ファイル名の生成に失敗しました')
+}Also applies to: 54-54
…ection 会話履歴更新時にSupabaseにデータを保存 & ローカルLLM修正
Summary by CodeRabbit
リリースノート
新機能
バグ修正
ドキュメント
依存関係の更新