feat(ollama): improve connection check method and provide selector for user to control model options (#1397)

* 🐛 fix(ollama): change checker with ollama's tags api

* feat(ollama): add error card to pull model

* 🚚 chore: move files

* 💄 style: update llava logo

* 🐛 fix: add ollama service unavailable error type

* 🐛 fix: ollama show passed with error message exists

*  feat(ollama): add download moniter to show speed and eta remaining time

* 🚨 ci: fix lint

* 💄 style: improve download style

* 🌐 style: add i18n

---------

Co-authored-by: shijianyue <shijianyue@bytedance.com>
Co-authored-by: arvinxx <arvinx@foxmail.com>
This commit is contained in:
Johnson
2024-03-15 22:47:21 +08:00
committed by GitHub
parent e472d6e827
commit 675902f5a8
58 changed files with 747 additions and 59 deletions
+8
View File
@@ -53,6 +53,7 @@
"MoonshotBizError": "حدث خطأ في خدمة جانب القمر، يرجى التحقق من المعلومات أدناه أو إعادة المحاولة",
"NoOpenAIAPIKey": "مفتاح API الخاص بـ OpenAI فارغ، يرجى إضافة مفتاح API الخاص بـ OpenAI",
"OllamaBizError": "خطأ في طلب خدمة Ollama، يرجى التحقق من المعلومات التالية أو إعادة المحاولة",
"OllamaServiceUnavailable": "خدمة Ollama غير متوفرة، يرجى التحقق مما إذا كانت قد تم تشغيلها بشكل صحيح",
"OpenAIBizError": "حدث خطأ في طلب خدمة OpenAI، يرجى التحقق من المعلومات أدناه وإعادة المحاولة",
"PerplexityBizError": "خطأ في طلب خدمة Perplexity AI، يرجى التحقق من المعلومات التالية أو إعادة المحاولة",
"PluginApiNotFound": "عذرًا، لا يوجد API للإضافة في وصف الإضافة، يرجى التحقق من تطابق طريقة الطلب الخاصة بك مع API الوصف",
@@ -114,6 +115,13 @@
},
"closeMessage": "إغلاق الرسالة",
"confirm": "تأكيد وإعادة المحاولة",
"model": {
"Ollama": {
"confirm": "تحميل",
"description": "أدخل علامة نموذج Ollama الخاصة بك لاستكمال الجلسة",
"title": "تحميل نموذج Ollama المحدد"
}
},
"oauth": {
"description": "فتح المسؤول توثيق تسجيل الدخول الموحد، انقر فوق الزر أدناه لتسجيل الدخول وفتح التطبيق",
"success": "تم تسجيل الدخول بنجاح",
+9 -1
View File
@@ -193,6 +193,14 @@
},
"waitingForMore": "يتم <1>التخطيط لتوفير</1> المزيد من النماذج، ترقبوا المزيد ✨"
},
"ollama": {
"download": {
"desc": "جارٍ تنزيل النموذج، يرجى عدم إغلاق هذه الصفحة. سيتم استئناف التنزيل من حيث توقف في حالة إعادة التنزيل",
"remainingTime": "الوقت المتبقي",
"speed": "سرعة التنزيل",
"title": "جارٍ تنزيل النموذج {{model}}"
}
},
"plugin": {
"addTooltip": "إضافة البرنامج المساعد",
"clearDeprecated": "مسح البرامج المساعدة الغير صالحة",
@@ -428,4 +436,4 @@
},
"title": "أدوات الامتداد"
}
}
}
+8
View File
@@ -53,6 +53,7 @@
"MoonshotBizError": "Fehler beim Abrufen des Dark Side of the Moon-Services. Bitte überprüfen Sie die folgenden Informationen oder versuchen Sie es erneut.",
"NoOpenAIAPIKey": "Der OpenAI-API-Schlüssel ist leer. Bitte fügen Sie einen benutzerdefinierten OpenAI-API-Schlüssel hinzu",
"OllamaBizError": "Fehler bei der Anforderung des Ollama-Dienstes. Bitte überprüfen Sie die folgenden Informationen oder versuchen Sie es erneut.",
"OllamaServiceUnavailable": "Ollama-Dienst nicht verfügbar. Bitte überprüfen Sie, ob er ordnungsgemäß gestartet wurde.",
"OpenAIBizError": "Fehler bei der OpenAI-Serviceanfrage. Bitte überprüfen Sie die folgenden Informationen oder versuchen Sie es erneut",
"PerplexityBizError": "Fehler bei der Anforderung des Perplexity AI-Dienstes. Bitte überprüfen Sie die folgenden Informationen oder versuchen Sie es erneut.",
"PluginApiNotFound": "Entschuldigung, das API des Plugins im Plugin-Manifest existiert nicht. Bitte überprüfen Sie, ob Ihre Anfragemethode mit dem Plugin-Manifest-API übereinstimmt",
@@ -114,6 +115,13 @@
},
"closeMessage": "Hinweis schließen",
"confirm": "Bestätigen und erneut versuchen",
"model": {
"Ollama": {
"confirm": "Herunterladen",
"description": "Geben Sie Ihre Ollama-Modellbezeichnung ein, um fortzufahren",
"title": "Bestimmtes Ollama-Modell herunterladen"
}
},
"oauth": {
"description": "Der Administrator hat die einheitliche Anmeldeauthentifizierung aktiviert. Klicken Sie unten auf die Schaltfläche, um sich anzumelden und die App zu entsperren.",
"success": "Anmeldung erfolgreich",
+9 -1
View File
@@ -193,6 +193,14 @@
},
"waitingForMore": "Weitere Modelle werden <1>geplant</1>, bitte freuen Sie sich auf weitere Updates ✨"
},
"ollama": {
"download": {
"desc": "Ollama lädt dieses Modell herunter. Bitte schließen Sie diese Seite nicht. Der Download wird an der abgebrochenen Stelle fortgesetzt, wenn Sie ihn erneut starten.",
"remainingTime": "Verbleibende Zeit",
"speed": "Download-Geschwindigkeit",
"title": "Modell {{model}} wird heruntergeladen"
}
},
"plugin": {
"addTooltip": "Benutzerdefiniertes Plugin",
"clearDeprecated": "Entfernen Sie ungültige Plugins",
@@ -428,4 +436,4 @@
},
"title": "Erweiterungswerkzeuge"
}
}
}
+8
View File
@@ -53,6 +53,7 @@
"MoonshotBizError": "There was an error with the Moonshot service, please troubleshoot or retry based on the following information.",
"NoOpenAIAPIKey": "OpenAI API Key is empty, please add a custom OpenAI API Key",
"OllamaBizError": "Error requesting Ollama service, please troubleshoot or retry based on the following information",
"OllamaServiceUnavailable": "Ollama service not detected, please check if it is running properly",
"OpenAIBizError": "Error requesting OpenAI service. Please troubleshoot or retry based on the following information.",
"PerplexityBizError": "Error requesting Perplexity AI service. Please troubleshoot or retry based on the following information.",
"PluginApiNotFound": "Sorry, the API does not exist in the plugin's manifest. Please check if your request method matches the plugin manifest API",
@@ -114,6 +115,13 @@
},
"closeMessage": "Close message",
"confirm": "Confirm and Retry",
"model": {
"Ollama": {
"confirm": "Download",
"description": "Enter your Ollama model label to proceed with the conversation",
"title": "Download specified Ollama model"
}
},
"oauth": {
"description": "The administrator has enabled unified login authentication. Click the button below to log in and unlock the application.",
"success": "Login successful",
+9 -1
View File
@@ -193,6 +193,14 @@
},
"waitingForMore": "More models are <1>planned to be added</1>, stay tuned ✨"
},
"ollama": {
"download": {
"desc": "Ollama is currently downloading the model. Please try not to close this page. It will resume from where it left off if you restart the download.",
"remainingTime": "Remaining Time",
"speed": "Download Speed",
"title": "Downloading model {{model}}"
}
},
"plugin": {
"addTooltip": "Custom Plugin",
"clearDeprecated": "Remove Deprecated Plugins",
@@ -428,4 +436,4 @@
},
"title": "Extension Tools"
}
}
}
+8
View File
@@ -53,6 +53,7 @@
"MoonshotBizError": "Se produjo un error al solicitar el servicio de Moonshot en el lado oscuro de la luna. Por favor, revise la siguiente información o inténtelo de nuevo.",
"NoOpenAIAPIKey": "La clave de API de OpenAI está vacía. Agregue una clave de API de OpenAI personalizada",
"OllamaBizError": "Error al solicitar el servicio de Ollama, por favor verifica la siguiente información o inténtalo de nuevo",
"OllamaServiceUnavailable": "Servicio Ollama no disponible: Ollama no detectado. Por favor, verifica si está iniciado correctamente.",
"OpenAIBizError": "Error al solicitar el servicio OpenAI. Depure o reintente según la siguiente información",
"PerplexityBizError": "Error comercial al solicitar el servicio de IA de Perplexity. Por favor, revisa la siguiente información o inténtalo de nuevo",
"PluginApiNotFound": "Lo sentimos, el API especificado no existe en el manifiesto del complemento. Verifique si su método de solicitud coincide con el API del manifiesto del complemento",
@@ -114,6 +115,13 @@
},
"closeMessage": "Cerrar mensaje",
"confirm": "Confirmar y volver a intentar",
"model": {
"Ollama": {
"confirm": "Descargar",
"description": "Ingresa las etiquetas de tu modelo Ollama para continuar la sesión",
"title": "Descargar el modelo Ollama especificado"
}
},
"oauth": {
"description": "El administrador ha habilitado la autenticación de inicio de sesión única. Haz clic en el botón a continuación para iniciar sesión y desbloquear la aplicación.",
"success": "Inicio de sesión exitoso",
+9 -1
View File
@@ -193,6 +193,14 @@
},
"waitingForMore": "Más modelos están en <1>planificación para su incorporación</1>, ¡estén atentos! ✨"
},
"ollama": {
"download": {
"desc": "Ollama está descargando este modelo. Por favor, no cierres esta página. La descarga se reanudará desde donde se detuvo si se reinicia.",
"remainingTime": "Tiempo restante",
"speed": "Velocidad de descarga",
"title": "Descargando el modelo {{model}}"
}
},
"plugin": {
"addTooltip": "Agregar complemento personalizado",
"clearDeprecated": "Eliminar complementos obsoletos",
@@ -428,4 +436,4 @@
},
"title": "Herramientas de extensión"
}
}
}
+8
View File
@@ -53,6 +53,7 @@
"MoonshotBizError": "Erreur de service Moonshot : une erreur s'est produite lors de la demande du service Côté Obscur de la Lune. Veuillez vérifier les informations suivantes ou réessayer.",
"NoOpenAIAPIKey": "La clé API OpenAI est vide. Veuillez ajouter une clé API OpenAI personnalisée",
"OllamaBizError": "Erreur commerciale lors de la demande de service Ollama, veuillez vérifier les informations ci-dessous ou réessayer",
"OllamaServiceUnavailable": "Service Ollama non disponible, veuillez vérifier s'il est démarré correctement",
"OpenAIBizError": "Erreur de service OpenAI. Veuillez diagnostiquer ou réessayer en fonction des informations ci-dessous",
"PerplexityBizError": "Erreur commerciale lors de la demande de service Perplexity AI. Veuillez vérifier les informations suivantes ou réessayer.",
"PluginApiNotFound": "Désolé, l'API spécifiée n'existe pas dans le manifeste du plugin. Veuillez vérifier que votre méthode de requête correspond à l'API du manifeste du plugin",
@@ -114,6 +115,13 @@
},
"closeMessage": "Fermer le message",
"confirm": "Confirmer et réessayer",
"model": {
"Ollama": {
"confirm": "Télécharger",
"description": "Saisissez l'étiquette de votre modèle Ollama pour continuer la session",
"title": "Télécharger le modèle Ollama spécifié"
}
},
"oauth": {
"description": "L'administrateur a activé l'authentification de connexion unique. Cliquez sur le bouton ci-dessous pour vous connecter et déverrouiller l'application.",
"success": "Connexion réussie",
+9 -1
View File
@@ -193,6 +193,14 @@
},
"waitingForMore": "Plus de modèles sont en cours de <1>planification pour être ajoutés</1>, restez à l'écoute ✨"
},
"ollama": {
"download": {
"desc": "Ollama est en train de télécharger ce modèle. Veuillez ne pas fermer cette page. Le téléchargement reprendra là où il s'est arrêté en cas de reprise.",
"remainingTime": "Temps restant",
"speed": "Vitesse de téléchargement",
"title": "Téléchargement du modèle {{model}} en cours"
}
},
"plugin": {
"addTooltip": "Ajouter un plugin personnalisé",
"clearDeprecated": "Effacer les plugins obsolètes",
@@ -428,4 +436,4 @@
},
"title": "Outils supplémentaires"
}
}
}
+8
View File
@@ -53,6 +53,7 @@
"MoonshotBizError": "Si è verificato un errore nel servizio Moonshot, si prega di controllare le informazioni seguenti o riprovare",
"NoOpenAIAPIKey": "La chiave API OpenAI è vuota. Aggiungi una chiave API personalizzata OpenAI",
"OllamaBizError": "Errore di servizio Ollama, controllare le informazioni seguenti o riprovare",
"OllamaServiceUnavailable": "Servizio Ollama non disponibile: controlla se è avviato correttamente",
"OpenAIBizError": "Errore nella richiesta del servizio OpenAI. Segui le informazioni seguenti per individuare e riprovare",
"PerplexityBizError": "Errore di business nella richiesta del servizio Perplexity AI, controlla le informazioni seguenti o riprova",
"PluginApiNotFound": "Spiacenti, l'API specificata non esiste nel manifesto del plugin. Verifica che il metodo di richiesta corrisponda all'API del manifesto del plugin",
@@ -114,6 +115,13 @@
},
"closeMessage": "Chiudi messaggio",
"confirm": "Conferma e riprova",
"model": {
"Ollama": {
"confirm": "Scarica",
"description": "Inserisci l'etichetta del tuo modello Ollama per continuare la sessione",
"title": "Scarica il modello Ollama specificato"
}
},
"oauth": {
"description": "L'amministratore ha abilitato l'autenticazione di accesso unificata. Fai clic sul pulsante sottostante per accedere e sbloccare l'applicazione.",
"success": "Accesso riuscito",
+9 -1
View File
@@ -193,6 +193,14 @@
},
"waitingForMore": "Altri modelli sono in fase di <1> pianificazione per l'integrazione </1>, resta sintonizzato ✨"
},
"ollama": {
"download": {
"desc": "Ollama sta scaricando questo modello. Si prega di non chiudere questa pagina. Il download verrà ripreso dal punto in cui è stato interrotto in caso di riavvio.",
"remainingTime": "Tempo rimanente",
"speed": "Velocità di download",
"title": "Download del modello {{model}} in corso"
}
},
"plugin": {
"addTooltip": "Aggiungi plugin personalizzato",
"clearDeprecated": "Rimuovi plugin non validi",
@@ -428,4 +436,4 @@
},
"title": "Strumenti aggiuntivi"
}
}
}
+8
View File
@@ -53,6 +53,7 @@
"MoonshotBizError": "月の裏側サービスのリクエストでエラーが発生しました。以下の情報を確認して再試行してください。",
"NoOpenAIAPIKey": "OpenAI APIキーが空です。カスタムOpenAI APIキーを追加してください。",
"OllamaBizError": "Ollamaサービスのリクエストでエラーが発生しました。以下の情報に基づいてトラブルシューティングを行うか、再度お試しください",
"OllamaServiceUnavailable": "Ollamaサービスが利用できません。正常に起動しているかどうかを確認してください",
"OpenAIBizError": "OpenAIサービスのリクエストエラーが発生しました。以下の情報に基づいて問題を解決したり、再試行したりしてください",
"PerplexityBizError": "Perplexity AIサービスのリクエストでエラーが発生しました。以下の情報に基づいてトラブルシューティングするか、再度お試しください",
"PluginApiNotFound": "申し訳ありませんが、プラグインのマニフェストに指定されたAPIが見つかりませんでした。リクエストメソッドとプラグインのマニフェストのAPIが一致しているかどうかを確認してください",
@@ -114,6 +115,13 @@
},
"closeMessage": "ヒントを閉じる",
"confirm": "確認して再試行",
"model": {
"Ollama": {
"confirm": "ダウンロード",
"description": "Ollamaモデルのラベルを入力して、会話を続行してください",
"title": "指定のOllamaモデルをダウンロード"
}
},
"oauth": {
"description": "管理者が統一ログイン認証を有効にしました。下のボタンをクリックしてログインすると、アプリがロック解除されます。",
"success": "ログインに成功しました",
+9 -1
View File
@@ -193,6 +193,14 @@
},
"waitingForMore": "さらに多くのモデルが <1>計画されています</1>。お楽しみに ✨"
},
"ollama": {
"download": {
"desc": "Ollama はモデルをダウンロード中です。ページを閉じないようにしてください。再度ダウンロードすると、中断した箇所から再開されます。",
"remainingTime": "残り時間",
"speed": "ダウンロード速度",
"title": "モデル {{model}} をダウンロード中"
}
},
"plugin": {
"addTooltip": "カスタムプラグイン",
"clearDeprecated": "無効なプラグインをクリア",
@@ -428,4 +436,4 @@
},
"title": "拡張ツール"
}
}
}
+8
View File
@@ -53,6 +53,7 @@
"MoonshotBizError": "요청한 문샷 비즈니스에 오류가 발생했습니다. 아래 정보를 확인하고 다시 시도해주세요.",
"NoOpenAIAPIKey": "OpenAI API 키가 비어 있습니다. 사용자 정의 OpenAI API 키를 추가해주세요.",
"OllamaBizError": "Ollama 서비스 요청 중 오류가 발생했습니다. 아래 정보를 확인하고 다시 시도하십시오.",
"OllamaServiceUnavailable": "Ollama 서비스를 찾을 수 없습니다. 정상적으로 시작되었는지 확인하십시오.",
"OpenAIBizError": "OpenAI 서비스 요청 중 오류가 발생했습니다. 아래 정보를 확인하고 문제를 해결하거나 다시 시도해주세요.",
"PerplexityBizError": "Perplexity AI 서비스 요청 중 오류가 발생했습니다. 아래 정보를 확인하고 다시 시도하십시오.",
"PluginApiNotFound": "죄송합니다. 플러그인 설명서에 해당 API가 없습니다. 요청 메서드와 플러그인 설명서 API가 일치하는지 확인해주세요.",
@@ -114,6 +115,13 @@
},
"closeMessage": "알림 닫기",
"confirm": "확인 및 다시 시도",
"model": {
"Ollama": {
"confirm": "다운로드",
"description": "Ollama 모델 태그를 입력하여 세션을 계속할 수 있습니다.",
"title": "지정된 Ollama 모델 다운로드"
}
},
"oauth": {
"description": "관리자가 통합 로그인 인증을 활성화했습니다. 아래 버튼을 클릭하여 로그인하면 앱을 잠금 해제할 수 있습니다.",
"success": "로그인 성공",
+9 -1
View File
@@ -193,6 +193,14 @@
},
"waitingForMore": "<1>계획에 따라 더 많은 모델이 추가될 예정</1>이니 기대해 주세요 ✨"
},
"ollama": {
"download": {
"desc": "Ollama 모델을 다운로드 중입니다. 이 페이지를 닫지 말아주세요. 다시 다운로드하면 중단된 곳부터 계속됩니다.",
"remainingTime": "남은 시간",
"speed": "다운로드 속도",
"title": "{{model}} 모델 다운로드 중"
}
},
"plugin": {
"addTooltip": "플러그인 추가",
"clearDeprecated": "사용되지 않는 플러그인 제거",
@@ -428,4 +436,4 @@
},
"title": "확장 도구"
}
}
}
+8
View File
@@ -53,6 +53,7 @@
"MoonshotBizError": "请求月球AI服务出错,请根据以下信息排查或重试",
"NoOpenAIAPIKey": "OpenAI API-sleutel ontbreekt. Voeg een aangepaste OpenAI API-sleutel toe",
"OllamaBizError": "Fout bij het aanroepen van de Ollama-service, controleer de onderstaande informatie en probeer opnieuw",
"OllamaServiceUnavailable": "Ollama 服务不可用,请检查是否已正常启动",
"OpenAIBizError": "Fout bij het aanvragen van OpenAI-service. Controleer de onderstaande informatie voor probleemoplossing of probeer opnieuw",
"PerplexityBizError": "Er is een fout opgetreden bij het aanvragen van de Perplexity AI-service. Controleer de onderstaande informatie en probeer het opnieuw.",
"PluginApiNotFound": "Sorry, de API van de plug-inbeschrijvingslijst bestaat niet. Controleer of uw verzoeksmethode overeenkomt met de plug-inbeschrijvingslijst API",
@@ -114,6 +115,13 @@
},
"closeMessage": "Sluit bericht",
"confirm": "Bevestigen en opnieuw proberen",
"model": {
"Ollama": {
"confirm": "下载",
"description": "输入您的 Ollama 模型标签,完成后即可继续会话",
"title": "下载指定的 Ollama 模型"
}
},
"oauth": {
"description": "De beheerder heeft een uniforme aanmeldingsverificatie ingeschakeld. Klik op de onderstaande knop om in te loggen en de app te ontgrendelen.",
"success": "Succesvol ingelogd",
+9 -1
View File
@@ -193,6 +193,14 @@
},
"waitingForMore": "Meer modellen worden <1>gepland om te worden toegevoegd</1>, dus blijf op de hoogte ✨"
},
"ollama": {
"download": {
"desc": "Ollama is bezig met het downloaden van dit model. Gelieve deze pagina niet te sluiten. Het downloaden zal worden hervat vanaf het onderbroken punt als u opnieuw begint te downloaden.",
"remainingTime": "Resterende tijd",
"speed": "Downloadsnelheid",
"title": "Model {{model}} wordt gedownload"
}
},
"plugin": {
"addTooltip": "Voeg aangepaste plug-in toe",
"clearDeprecated": "Verwijder verouderde plug-ins",
@@ -428,4 +436,4 @@
},
"title": "Uitbreidingsgereedschap"
}
}
}
+8
View File
@@ -53,6 +53,7 @@
"MoonshotBizError": "请求月球AI服务出错,请根据以下信息排查或重试",
"NoOpenAIAPIKey": "Klucz API OpenAI jest pusty. Proszę dodać niestandardowy klucz API OpenAI",
"OllamaBizError": "Błąd usługi Ollama, sprawdź poniższe informacje lub spróbuj ponownie",
"OllamaServiceUnavailable": "Usługa Ollama jest niedostępna. Sprawdź, czy została poprawnie uruchomiona.",
"OpenAIBizError": "Błąd żądania usługi OpenAI. Proszę sprawdź poniższe informacje i spróbuj ponownie",
"PerplexityBizError": "Błąd biznesowy podczas żądania usługi Perplexity AI. Sprawdź poniższe informacje lub spróbuj ponownie.",
"PluginApiNotFound": "Przepraszamy, w manifestach wtyczki nie istnieje to API. Proszę sprawdź, czy metoda żądania jest zgodna z API w manifestach wtyczki",
@@ -114,6 +115,13 @@
},
"closeMessage": "Zamknij komunikat",
"confirm": "Potwierdź i spróbuj ponownie",
"model": {
"Ollama": {
"confirm": "Pobierz",
"description": "Wprowadź etykietę modelu Ollama, aby kontynuować sesję.",
"title": "Pobierz określony model Ollama"
}
},
"oauth": {
"description": "Administrator włączył jednolite uwierzytelnianie logowania. Kliknij poniższy przycisk, aby się zalogować i odblokować aplikację.",
"success": "Zalogowano pomyślnie",
+9 -1
View File
@@ -193,6 +193,14 @@
},
"waitingForMore": "Więcej modeli jest obecnie w <1>planach dołączenia</1>, prosimy o cierpliwość ✨"
},
"ollama": {
"download": {
"desc": "Ollama pobiera ten model. Prosimy nie zamykać tej strony. Gdy pobieranie zostanie wznowione, będzie kontynuowane od miejsca przerwania.",
"remainingTime": "Czas pozostały",
"speed": "Prędkość pobierania",
"title": "Pobieranie modelu {{model}}"
}
},
"plugin": {
"addTooltip": "Dodaj niestandardowy dodatek",
"clearDeprecated": "Usuń przestarzałe dodatki",
@@ -428,4 +436,4 @@
},
"title": "Narzędzia rozszerzeń"
}
}
}
+8
View File
@@ -53,6 +53,7 @@
"MoonshotBizError": "O serviço Moonshot na face oculta da lua encontrou um erro. Por favor, verifique as informações abaixo ou tente novamente.",
"NoOpenAIAPIKey": "A chave de API do OpenAI está em branco. Adicione uma chave de API personalizada do OpenAI",
"OllamaBizError": "Erro de negócio ao solicitar o serviço Ollama, verifique as informações a seguir ou tente novamente",
"OllamaServiceUnavailable": "O serviço Ollama não está disponível, verifique se está iniciado corretamente",
"OpenAIBizError": "Erro ao solicitar o serviço OpenAI. Verifique ou tente novamente com base nas informações abaixo",
"PerplexityBizError": "Erro de negócios ao solicitar o serviço de IA Perplexity, verifique as informações a seguir ou tente novamente",
"PluginApiNotFound": "Desculpe, o API especificado não existe no manifesto do plugin. Verifique se o método de solicitação corresponde ao API do manifesto do plugin",
@@ -114,6 +115,13 @@
},
"closeMessage": "Fechar mensagem",
"confirm": "Confirmar e tentar novamente",
"model": {
"Ollama": {
"confirm": "Baixar",
"description": "Digite as tags do seu modelo Ollama para continuar a sessão",
"title": "Baixar o modelo Ollama especificado"
}
},
"oauth": {
"description": "O administrador ativou a autenticação de login unificado. Clique no botão abaixo para fazer login e desbloquear o aplicativo.",
"success": "Login bem-sucedido",
+9 -1
View File
@@ -193,6 +193,14 @@
},
"waitingForMore": "Mais modelos estão sendo <1>planejados para serem adicionados</1>, aguarde ansiosamente ✨"
},
"ollama": {
"download": {
"desc": "Ollama está baixando este modelo. Por favor, evite fechar esta página. O download será retomado do ponto em que parou, caso seja reiniciado.",
"remainingTime": "Tempo restante",
"speed": "Velocidade de download",
"title": "Baixando modelo {{model}}"
}
},
"plugin": {
"addTooltip": "Adicionar plug-in personalizado",
"clearDeprecated": "Remover plug-ins inválidos",
@@ -428,4 +436,4 @@
},
"title": "Ferramentas de Extensão"
}
}
}
+8
View File
@@ -53,6 +53,7 @@
"MoonshotBizError": "请求月球暗面服务出错,请根据以下信息排查或重试",
"NoOpenAIAPIKey": "Ключ OpenAI API пуст, пожалуйста, добавьте свой собственный ключ OpenAI API",
"OllamaBizError": "Ошибка обращения к сервису Ollama, пожалуйста, проверьте следующую информацию или повторите попытку",
"OllamaServiceUnavailable": "Сервис Ollama недоступен. Пожалуйста, проверьте, запущен ли он корректно.",
"OpenAIBizError": "Ошибка запроса службы OpenAI. Устраните неполадку или повторите попытку, основываясь на следующей информации.",
"PerplexityBizError": "Ошибка обращения к сервису Perplexity AI. Пожалуйста, проверьте информацию ниже или повторите попытку",
"PluginApiNotFound": "К сожалению, API не существует в манифесте плагина. Пожалуйста, проверьте, соответствует ли ваш метод запроса API манифеста плагина",
@@ -114,6 +115,13 @@
},
"closeMessage": "Закрыть сообщение",
"confirm": "Подтвердить и повторить попытку",
"model": {
"Ollama": {
"confirm": "Скачать",
"description": "Введите метку вашей модели Ollama, чтобы продолжить сеанс",
"title": "Скачать указанную модель Ollama"
}
},
"oauth": {
"description": "Администратор включил единую систему аутентификации. Нажмите кнопку ниже, чтобы войти и разблокировать приложение.",
"success": "Успешный вход",
+9 -1
View File
@@ -193,6 +193,14 @@
},
"waitingForMore": "Больше моделей доступно в <1>плане подключения</1>, ожидайте ✨"
},
"ollama": {
"download": {
"desc": "Ollama загружает эту модель. Пожалуйста, не закрывайте эту страницу. Загрузка будет возобновлена с того же места, если вы решите начать её заново.",
"remainingTime": "Оставшееся время",
"speed": "Скорость загрузки",
"title": "Загрузка модели {{model}}"
}
},
"plugin": {
"addTooltip": "Добавить настраиваемый плагин",
"clearDeprecated": "Удалить устаревшие плагины",
@@ -428,4 +436,4 @@
},
"title": "Дополнительные инструменты"
}
}
}
+8
View File
@@ -53,6 +53,7 @@
"MoonshotBizError": "Moonshot hizmetinde bir hata oluştu, lütfen aşağıdaki bilgilere göre sorunu giderin veya tekrar deneyin",
"NoOpenAIAPIKey": "OpenAI API Anahtarı boş, lütfen özel bir OpenAI API Anahtarı ekleyin",
"OllamaBizError": "Ollama servisine yapılan istekte hata oluştu, lütfen aşağıdaki bilgilere göre sorunu gidermeye çalışın veya tekrar deneyin",
"OllamaServiceUnavailable": "Ollama 服务不可用,请检查是否已正常启动",
"OpenAIBizError": "OpenAI hizmeti talep ederken hata oluştu. Aşağıdaki bilgilere dayanarak sorun giderin veya tekrar deneyin.",
"PerplexityBizError": "Perplexity AI hizmetine yapılan istekte hata oluştu, lütfen aşağıdaki bilgilere göre sorunu gidermeye çalışın veya tekrar deneyin",
"PluginApiNotFound": "Üzgünüm, eklentinin bildiriminde API mevcut değil. Lütfen istek yönteminizin eklenti bildirim API'sı ile eşleşip eşleşmediğini kontrol edin",
@@ -114,6 +115,13 @@
},
"closeMessage": "Mesajı kapat",
"confirm": "Onayla ve Yeniden Dene",
"model": {
"Ollama": {
"confirm": "下载",
"description": "Ollama model etiketinizi girin ve devam etmek için tamamlayın",
"title": "Belirli Ollama modelini indir"
}
},
"oauth": {
"description": "Yönetici, tek oturum açma kimlik doğrulamasını etkinleştirdi. Aşağıdaki düğmeye tıklayarak giriş yapabilir ve uygulamayı kilidini açabilirsiniz.",
"success": "Giriş başarılı",
+9 -1
View File
@@ -193,6 +193,14 @@
},
"waitingForMore": "Daha fazla model eklenmesi planlanıyor ✨"
},
"ollama": {
"download": {
"desc": "Ollama bu modeli indiriyor, lütfen bu sayfayı kapatmamaya çalışın. Yeniden indirme durumunda kaldığı yerden devam edecektir.",
"remainingTime": "Kalan Zaman",
"speed": "İndirme Hızı",
"title": "{{model}} Modeli İndiriliyor"
}
},
"plugin": {
"addTooltip": "Eklenti Ekle",
"clearDeprecated": "Kullanım Dışı Eklentileri Kaldır",
@@ -428,4 +436,4 @@
},
"title": "Uzantı Araçları"
}
}
}
+8
View File
@@ -53,6 +53,7 @@
"MoonshotBizError": "Yêu cầu dịch vụ Mặt Trăng Tối gặp sự cố, vui lòng kiểm tra thông tin dưới đây hoặc thử lại",
"NoOpenAIAPIKey": "Khóa API OpenAI trống, vui lòng thêm Khóa API OpenAI tùy chỉnh",
"OllamaBizError": "Yêu cầu dịch vụ Ollama gặp lỗi, vui lòng kiểm tra thông tin dưới đây hoặc thử lại",
"OllamaServiceUnavailable": "Dịch vụ Ollama không khả dụng, vui lòng kiểm tra xem nó đã được khởi động chưa",
"OpenAIBizError": "Yêu cầu dịch vụ OpenAI gặp lỗi, vui lòng xác minh hoặc thử lại dựa trên thông tin dưới đây",
"PerplexityBizError": "Yêu cầu dịch vụ AI Perplexity gặp lỗi, vui lòng kiểm tra thông tin dưới đây hoặc thử lại sau",
"PluginApiNotFound": "Xin lỗi, không có API nào trong tệp mô tả plugin, vui lòng kiểm tra phương thức yêu cầu của bạn có khớp với API mô tả plugin không",
@@ -114,6 +115,13 @@
},
"closeMessage": "Đóng thông báo",
"confirm": "Xác nhận và thử lại",
"model": {
"Ollama": {
"confirm": "Tải xuống",
"description": "Nhập nhãn mô hình Ollama của bạn để tiếp tục cuộc trò chuyện",
"title": "Tải xuống mô hình Ollama cụ thể"
}
},
"oauth": {
"description": "Quản trị viên đã mở tính năng xác thực đăng nhập thống nhất. Nhấn vào nút bên dưới để đăng nhập và mở khóa ứng dụng",
"success": "Đăng nhập thành công",
+9 -1
View File
@@ -193,6 +193,14 @@
},
"waitingForMore": "Có thêm mô hình đang <1>được lên kế hoạch tích hợp</1>, hãy chờ đợi ✨"
},
"ollama": {
"download": {
"desc": "Ollama đang tải xuống mô hình này, vui lòng không đóng trang này. Quá trình tải xuống sẽ tiếp tục từ nơi đã bị gián đoạn khi tải lại",
"remainingTime": "Thời gian còn lại",
"speed": "Tốc độ tải xuống",
"title": "Đang tải xuống mô hình {{model}}"
}
},
"plugin": {
"addTooltip": "Thêm tiện ích",
"clearDeprecated": "Xóa tiện ích không còn hỗ trợ",
@@ -428,4 +436,4 @@
},
"title": "Công cụ mở rộng"
}
}
}
+8
View File
@@ -67,6 +67,7 @@
"AnthropicBizError": "请求 Anthropic AI 服务出错,请根据以下信息排查或重试",
"InvalidOllamaArgs": "Ollama 配置不正确,请检查 Ollama 配置后重试",
"OllamaBizError": "请求 Ollama 服务出错,请根据以下信息排查或重试",
"OllamaServiceUnavailable": "未检测到 Ollama 服务,请检查是否正常启动",
"AgentRuntimeError": "Lobe 语言模型运行时执行出错,请根据以下信息排查或重试"
},
"stt": {
@@ -114,6 +115,13 @@
},
"closeMessage": "关闭提示",
"confirm": "确认并重试",
"model": {
"Ollama": {
"confirm": "下载",
"description": "输入你的 Ollama 模型标签,完成即可继续会话",
"title": "下载指定的 Ollama 模型"
}
},
"oauth": {
"description": "管理员已开启统一登录认证,点击下方按钮登录,即可解锁应用",
"success": "登录成功",
+9 -1
View File
@@ -193,6 +193,14 @@
},
"waitingForMore": "更多模型正在 <1>计划接入</1> 中,敬请期待 ✨"
},
"ollama": {
"download": {
"desc": "Ollama 正在下载该模型,请尽量不要关闭本页面。重新下载时将会中断处继续",
"remainingTime": "剩余时间",
"speed": "下载速度",
"title": "正在下载模型 {{model}} "
}
},
"plugin": {
"addTooltip": "自定义插件",
"clearDeprecated": "移除无效插件",
@@ -428,4 +436,4 @@
},
"title": "扩展插件"
}
}
}
+8
View File
@@ -53,6 +53,7 @@
"MoonshotBizError": "請求月球背面服務出錯,請根據以下信息排查或重試",
"NoOpenAIAPIKey": "OpenAI API 金鑰為空,請添加自訂 OpenAI API 金鑰",
"OllamaBizError": "請求 Ollama 服務出錯,請根據以下資訊排查或重試",
"OllamaServiceUnavailable": "未偵測到 Ollama 服務,請檢查是否正常啟動",
"OpenAIBizError": "請求 OpenAI 服務出錯。請根據以下資訊進行排查或重試。",
"PerplexityBizError": "請求 Perplexity AI 服務出錯,請根據以下信息排查或重試",
"PluginApiNotFound": "抱歉,外掛描述檔案中不存在該 API。請檢查您的請求方法與外掛清單 API 是否相符",
@@ -114,6 +115,13 @@
},
"closeMessage": "關閉提示",
"confirm": "確認並重試",
"model": {
"Ollama": {
"confirm": "下載",
"description": "輸入你的 Ollama 模型標籤,完成即可繼續會話",
"title": "下載指定的 Ollama 模型"
}
},
"oauth": {
"description": "管理員已開啟統一登錄認證,點擊下方按鈕登錄,即可解鎖應用",
"success": "登錄成功",
+9 -1
View File
@@ -193,6 +193,14 @@
},
"waitingForMore": "更多模型正在 <1>計劃接入</1> 中,敬請期待 ✨"
},
"ollama": {
"download": {
"desc": "Ollama 正在下載該模型,請儘量不要關閉本頁面。重新下載時將會中斷處繼續",
"remainingTime": "剩餘時間",
"speed": "下載速度",
"title": "正在下載模型 {{model}}"
}
},
"plugin": {
"addTooltip": "新增自訂外掛程式",
"clearDeprecated": "清除已棄用的外掛",
@@ -428,4 +436,4 @@
},
"title": "擴展工具"
}
}
}
+1
View File
@@ -120,6 +120,7 @@
"next-sitemap": "^4",
"numeral": "^2",
"nuqs": "^1",
"ollama": "^0.5.0",
"openai": "^4.22",
"polished": "^4",
"posthog-js": "^1",
+3 -2
View File
@@ -10,6 +10,7 @@ export const runtime = 'edge';
*/
export const GET = async () => {
const {
ENABLE_LANGFUSE,
CUSTOM_MODELS,
ENABLED_MOONSHOT,
ENABLED_ZHIPU,
@@ -21,7 +22,7 @@ export const GET = async () => {
ENABLED_ANTHROPIC,
ENABLED_MISTRAL,
DEFAULT_AGENT_CONFIG,
ENABLE_LANGFUSE,
OLLAMA_CUSTOM_MODELS,
} = getServerConfig();
const config: GlobalServerConfig = {
@@ -37,7 +38,7 @@ export const GET = async () => {
google: { enabled: ENABLED_GOOGLE },
mistral: { enabled: ENABLED_MISTRAL },
moonshot: { enabled: ENABLED_MOONSHOT },
ollama: { enabled: ENABLE_OLLAMA },
ollama: { customModelName: OLLAMA_CUSTOM_MODELS, enabled: ENABLE_OLLAMA },
perplexity: { enabled: ENABLED_PERPLEXITY },
zhipu: { enabled: ENABLED_ZHIPU },
},
+2 -1
View File
@@ -1,5 +1,5 @@
import { AgentRuntimeErrorType, ILobeAgentRuntimeErrorType } from '@/libs/agent-runtime';
import { ErrorResponse, ErrorType } from '@/types/fetch';
import { ChatErrorType, ErrorResponse, ErrorType } from '@/types/fetch';
const getStatus = (errorType: ILobeAgentRuntimeErrorType | ErrorType) => {
// InvalidAccessCode / InvalidAzureAPIKey / InvalidOpenAIAPIKey / InvalidZhipuAPIKey ....
@@ -37,6 +37,7 @@ const getStatus = (errorType: ILobeAgentRuntimeErrorType | ErrorType) => {
case AgentRuntimeErrorType.MoonshotBizError: {
return 476;
}
case ChatErrorType.OllamaServiceUnavailable:
case AgentRuntimeErrorType.OllamaBizError: {
return 478;
}
+73
View File
@@ -0,0 +1,73 @@
import { CheckCircleFilled } from '@ant-design/icons';
import { Alert, Highlighter } from '@lobehub/ui';
import { Button } from 'antd';
import { useTheme } from 'antd-style';
import { ListResponse } from 'ollama/browser';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { Flexbox } from 'react-layout-kit';
import useSWR from 'swr';
import { useIsMobile } from '@/hooks/useIsMobile';
import { ollamaService } from '@/services/ollama';
const OllamaChecker = memo(() => {
const { t } = useTranslation('setting');
const theme = useTheme();
const { data, error, isLoading, mutate } = useSWR<ListResponse>(
'ollama.list',
ollamaService.getModels,
{
revalidateOnFocus: false,
revalidateOnMount: false,
revalidateOnReconnect: false,
},
);
const checkConnection = () => {
mutate();
};
const isMobile = useIsMobile();
return (
<Flexbox align={isMobile ? 'flex-start' : 'flex-end'} gap={8}>
<Flexbox align={'center'} direction={isMobile ? 'horizontal-reverse' : 'horizontal'} gap={12}>
{!error && data?.models && (
<Flexbox gap={4} horizontal>
<CheckCircleFilled
style={{
color: theme.colorSuccess,
}}
/>
{t('llm.checker.pass')}
</Flexbox>
)}
<Button loading={isLoading} onClick={checkConnection}>
{t('llm.checker.button')}
</Button>
</Flexbox>
{error && (
<Flexbox gap={8} style={{ maxWidth: '600px', width: '100%' }}>
<Alert
banner
extra={
<Flexbox>
<Highlighter copyButtonSize={'small'} language={'json'} type={'pure'}>
{JSON.stringify(error.body || error, null, 2)}
</Highlighter>
</Flexbox>
}
message={t(`response.${error.type}` as any, { ns: 'error' })}
showIcon
type={'error'}
/>
</Flexbox>
)}
</Flexbox>
);
});
export default OllamaChecker;
+2 -4
View File
@@ -4,9 +4,7 @@ import { useTheme } from 'antd-style';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { ModelProvider } from '@/libs/agent-runtime';
import Checker from '../components/Checker';
import Checker from './Checker';
import ProviderConfig from '../components/ProviderConfig';
import { LLMProviderBaseUrlKey, LLMProviderConfigKey } from '../const';
@@ -38,7 +36,7 @@ const OllamaProvider = memo(() => {
name: [LLMProviderConfigKey, providerKey, 'customModelName'],
},
{
children: <Checker model={'llama2'} provider={ModelProvider.Ollama} />,
children: <Checker />,
desc: t('llm.Ollama.checker.desc'),
label: t('llm.checker.title'),
minWidth: undefined,
+23 -17
View File
@@ -16,6 +16,28 @@ interface ConnectionCheckerProps {
provider: string;
}
const Error = memo<{ error: ChatMessageError }>(({ error }) => {
const { t } = useTranslation('error');
return (
<Flexbox gap={8} style={{ maxWidth: '600px', width: '100%' }}>
<Alert
banner
extra={
<Flexbox>
<Highlighter copyButtonSize={'small'} language={'json'} type={'pure'}>
{JSON.stringify(error.body || error, null, 2)}
</Highlighter>
</Flexbox>
}
message={t(`response.${error.type}` as any)}
showIcon
type={'error'}
/>
</Flexbox>
);
});
const Checker = memo<ConnectionCheckerProps>(({ model, provider }) => {
const { t } = useTranslation('setting');
@@ -75,23 +97,7 @@ const Checker = memo<ConnectionCheckerProps>(({ model, provider }) => {
{t('llm.checker.button')}
</Button>
</Flexbox>
{error && (
<Flexbox gap={8} style={{ maxWidth: '600px', width: '100%' }}>
<Alert
banner
extra={
<Flexbox>
<Highlighter copyButtonSize={'small'} language={'json'} type={'pure'}>
{JSON.stringify(error.body || error, null, 2)}
</Highlighter>
</Flexbox>
}
message={t(`response.${error.type}` as any, { ns: 'error' })}
showIcon
type={'error'}
/>
</Flexbox>
)}
{error && <Error error={error} />}
</Flexbox>
);
});
+2
View File
@@ -5,6 +5,7 @@ import {
Claude,
Gemini,
Gemma,
LLaVA,
Meta,
Minimax,
Mistral,
@@ -29,6 +30,7 @@ const ModelIcon = memo<ModelProviderIconProps>(({ model, size = 12 }) => {
if (model.includes('claude')) return <Claude.Avatar size={size} />;
if (model.includes('titan')) return <Aws.Avatar size={size} />;
if (model.includes('llama')) return <Meta.Avatar size={size} />;
if (model.includes('llava')) return <LLaVA.Avatar size={size} />;
if (model.includes('gemini')) return <Gemini.Avatar size={size} />;
if (model.includes('gemma')) return <Gemma.Avatar size={size} />;
if (model.includes('qwen')) return <Tongyi.Avatar background={Tongyi.colorPrimary} size={size} />;
+2
View File
@@ -5,6 +5,7 @@ import {
Claude,
Gemini,
Gemma,
LLaVA,
Meta,
Minimax,
Mistral,
@@ -28,6 +29,7 @@ const ModelIcon = memo<ModelIconProps>(({ model, size = 12 }) => {
if (model.includes('claude')) return <Claude size={size} />;
if (model.includes('titan')) return <Aws size={size} />;
if (model.includes('llama')) return <Meta size={size} />;
if (model.includes('llava')) return <LLaVA size={size} />;
if (model.includes('gemini')) return <Gemini size={size} />;
if (model.includes('gemma')) return <Gemma.Simple size={size} />;
if (model.includes('moonshot')) return <Moonshot size={size} />;
+14
View File
@@ -92,6 +92,20 @@ const Ollama: ModelProviderCard = {
tokens: 4800,
vision: false,
},
{
displayName: 'Mixtral 8x7B',
functionCall: false,
id: 'mixtral',
tokens: 32_000,
vision: false,
},
{
displayName: 'Qwen Chat 4B',
functionCall: false,
id: 'qwen',
tokens: 32_768,
vision: false,
},
{
displayName: 'Qwen Chat 7B',
functionCall: false,
+2
View File
@@ -47,6 +47,7 @@ declare global {
// Ollama Provider;
OLLAMA_PROXY_URL?: string;
OLLAMA_CUSTOM_MODELS?: string;
}
}
}
@@ -116,5 +117,6 @@ export const getProviderConfig = () => {
ENABLE_OLLAMA: !!process.env.OLLAMA_PROXY_URL,
OLLAMA_PROXY_URL: process.env.OLLAMA_PROXY_URL || '',
OLLAMA_CUSTOM_MODELS: process.env.OLLAMA_CUSTOM_MODELS,
};
};
@@ -0,0 +1,138 @@
import { Ollama } from '@lobehub/icons';
import { Button, Input, Progress } from 'antd';
import { useTheme } from 'antd-style';
import { memo, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Center, Flexbox } from 'react-layout-kit';
import useSWR from 'swr';
import { ollamaService } from '@/services/ollama';
import { useChatStore } from '@/store/chat';
import { ErrorActionContainer, FormAction } from '../style';
import { useDownloadMonitor } from './useDownloadMonitor';
interface OllamaModelFormProps {
id: string;
model: string;
}
const OllamaModelForm = memo<OllamaModelFormProps>(({ id, model }) => {
const { t } = useTranslation('error');
const { t: settingT } = useTranslation('setting');
const [modelToPull, setModelToPull] = useState(model);
const [completed, setCompleted] = useState(0);
const [total, setTotal] = useState(0);
const { remainingTime, downloadSpeed } = useDownloadMonitor(total, completed);
const percent = useMemo(() => {
return total ? Number(((completed / total) * 100).toFixed(0)) : 0;
}, [completed, total]);
const [delAndRegenerateMessage, deleteMessage] = useChatStore((s) => [
s.delAndRegenerateMessage,
s.deleteMessage,
]);
const theme = useTheme();
const { mutate, isLoading: isDownloading } = useSWR(
[id, modelToPull],
async ([, model]) => {
const generator = await ollamaService.pullModel(model);
for await (const progress of generator) {
if (progress.completed) {
setCompleted(progress.completed);
setTotal(progress.total);
}
}
return null;
},
{
onSuccess: () => {
delAndRegenerateMessage(id);
},
revalidateOnFocus: false,
revalidateOnMount: false,
},
);
return (
<Center gap={16} style={{ maxWidth: 300, width: '100%' }}>
<FormAction
avatar={<Ollama color={theme.colorPrimary} size={64} />}
description={
isDownloading ? settingT('ollama.download.desc') : t('unlock.model.Ollama.description')
}
title={
isDownloading
? settingT('ollama.download.title', { model: modelToPull })
: t('unlock.model.Ollama.title')
}
>
{!isDownloading && (
<Input
onChange={(e) => {
setModelToPull(e.target.value);
}}
value={modelToPull}
/>
)}
</FormAction>
{isDownloading && (
<Flexbox flex={1} gap={8} width={'100%'}>
<Progress
percent={percent}
showInfo
strokeColor={theme.colorSuccess}
trailColor={theme.colorSuccessBg}
/>
<Flexbox
distribution={'space-between'}
horizontal
style={{ color: theme.colorTextDescription, fontSize: 12 }}
>
<span>
{settingT('ollama.download.remainingTime')}: {remainingTime}
</span>
<span>
{settingT('ollama.download.speed')}: {downloadSpeed}
</span>
</Flexbox>
</Flexbox>
)}
<Flexbox gap={12} width={'100%'}>
<Button
block
loading={isDownloading}
onClick={() => {
mutate();
}}
style={{ marginTop: 8 }}
type={'primary'}
>
{t('unlock.model.Ollama.confirm')}
</Button>
<Button
onClick={() => {
deleteMessage(id);
}}
>
{t('unlock.closeMessage')}
</Button>
</Flexbox>
</Center>
);
});
interface InvalidOllamaModelProps {
id: string;
model: string;
}
const InvalidOllamaModel = memo<InvalidOllamaModelProps>(({ id, model }) => (
<ErrorActionContainer>
<OllamaModelForm id={id} model={model} />
</ErrorActionContainer>
));
export default InvalidOllamaModel;
@@ -0,0 +1,48 @@
import { useEffect, useMemo, useState } from 'react';
const formatSpeed = (speed: number): string => {
const kbPerSecond = speed / 1024;
if (kbPerSecond < 1024) {
return `${kbPerSecond.toFixed(1)} KB/s`;
} else {
const mbPerSecond = kbPerSecond / 1024;
return `${mbPerSecond.toFixed(1)} MB/s`;
}
};
const formatTime = (timeInSeconds: number): string => {
if (timeInSeconds < 60) {
return `${timeInSeconds.toFixed(1)} s`;
} else if (timeInSeconds < 3600) {
return `${(timeInSeconds / 60).toFixed(1)} min`;
} else {
return `${(timeInSeconds / 3600).toFixed(2)} h`;
}
};
export const useDownloadMonitor = (totalSize: number, completedSize: number) => {
const [startTime, setStartTime] = useState<number>(Date.now());
const [downloadSpeed, setDownloadSpeed] = useState<string>('0 KB/s');
const [remainingTime, setRemainingTime] = useState<string>('-');
const isReady = useMemo(() => completedSize > 0, [completedSize]);
useEffect(() => {
const currentTime = Date.now();
// mark as start download
if (isReady) {
const elapsedTime = (currentTime - startTime) / 1000; // in seconds
const speed = completedSize / elapsedTime; // in bytes per second
const remainingSize = totalSize - completedSize;
const time = remainingSize / speed; // in seconds
setDownloadSpeed(formatSpeed(speed));
setRemainingTime(formatTime(time));
} else {
setStartTime(currentTime);
}
}, [isReady, completedSize]);
return { downloadSpeed, remainingTime };
};
@@ -0,0 +1,34 @@
import { memo } from 'react';
import { ChatMessage } from '@/types/message';
import ErrorJsonViewer from './ErrorJsonViewer';
import InvalidModel from './InvalidOllamaModel';
interface OllamaError {
code: string | null;
message: string;
param?: any;
type: string;
}
interface OllamaErrorResponse {
error: OllamaError;
}
const UNRESOLVED_MODEL_REGEXP = /model '([\w+,-_]+)' not found/;
const OllamaBizError = memo<ChatMessage>(({ error, id }) => {
const errorBody: OllamaErrorResponse = (error as any)?.body;
const errorMessage = errorBody.error?.message;
const unresolvedModel = errorMessage?.match(UNRESOLVED_MODEL_REGEXP)?.[1];
if (unresolvedModel) {
return <InvalidModel id={id} model={unresolvedModel} />;
}
return <ErrorJsonViewer error={error} id={id} />;
});
export default OllamaBizError;
@@ -9,6 +9,7 @@ import { ChatMessage, ChatMessageError } from '@/types/message';
import ErrorJsonViewer from './ErrorJsonViewer';
import InvalidAPIKey from './InvalidAPIKey';
import InvalidAccessCode from './InvalidAccessCode';
import OllamaBizError from './OllamaBizError';
import OpenAiBizError from './OpenAiBizError';
import PluginSettings from './PluginSettings';
@@ -58,6 +59,10 @@ const ErrorMessageExtra = memo<{ data: ChatMessage }>(({ data }) => {
return <OpenAiBizError {...data} />;
}
case AgentRuntimeErrorType.OllamaBizError: {
return <OllamaBizError {...data} />;
}
case ChatErrorType.InvalidAccessCode: {
return <InvalidAccessCode id={data.id} provider={data.error?.body?.provider} />;
}
+2 -2
View File
@@ -36,14 +36,14 @@ export const FormAction = memo<{
const { styles, theme } = useStyles();
return (
<Center gap={16} style={{ maxWidth: 300 }}>
<Center gap={16} style={{ maxWidth: 300, width: '100%' }}>
<Avatar
avatar={avatar}
background={background ?? theme.colorFillContent}
gap={12}
size={80}
/>
<Flexbox style={{ fontSize: 20 }}>{title}</Flexbox>
<Flexbox style={{ fontSize: 20, textAlign: 'center' }}>{title}</Flexbox>
<Flexbox className={styles.desc}>{description}</Flexbox>
{children}
</Center>
+8
View File
@@ -84,6 +84,7 @@ export default {
InvalidOllamaArgs: 'Ollama 配置不正确,请检查 Ollama 配置后重试',
OllamaBizError: '请求 Ollama 服务出错,请根据以下信息排查或重试',
OllamaServiceUnavailable: '未检测到 Ollama 服务,请检查是否正常启动',
AgentRuntimeError: 'Lobe 语言模型运行时执行出错,请根据以下信息排查或重试',
/* eslint-enable */
@@ -134,6 +135,13 @@ export default {
},
closeMessage: '关闭提示',
confirm: '确认并重试',
model: {
Ollama: {
confirm: '下载',
description: '输入你的 Ollama 模型标签,完成即可继续会话',
title: '下载指定的 Ollama 模型',
},
},
oauth: {
description: '管理员已开启统一登录认证,点击下方按钮登录,即可解锁应用',
success: '登录成功',
+9 -1
View File
@@ -195,6 +195,14 @@ export default {
},
waitingForMore: '更多模型正在 <1>计划接入</1> 中,敬请期待 ✨',
},
ollama: {
download: {
desc: 'Ollama 正在下载该模型,请尽量不要关闭本页面。重新下载时将会中断处继续',
remainingTime: '剩余时间',
speed: '下载速度',
title: '正在下载模型 {{model}} ',
},
},
plugin: {
addTooltip: '自定义插件',
clearDeprecated: '移除无效插件',
@@ -410,6 +418,7 @@ export default {
placeholder: '请输入助手的标识符,需要是唯一的,比如 web-development',
tooltips: '分享到助手市场',
},
tab: {
about: '关于',
agent: '默认助手',
@@ -417,7 +426,6 @@ export default {
llm: '语言模型',
tts: '语音服务',
},
tools: {
builtins: {
groupName: '内置插件',
+26
View File
@@ -0,0 +1,26 @@
import { Mock, describe, expect, it, vi } from 'vitest';
import { ollamaService } from '../ollama';
vi.stubGlobal('fetch', vi.fn());
describe('OllamaService', () => {
describe('list models', async () => {
it('should make a GET request with the correct payload', async () => {
(fetch as Mock).mockResolvedValueOnce(new Response(JSON.stringify({ models: [] })));
expect(await ollamaService.getModels()).toEqual({ models: [] });
expect(global.fetch).toHaveBeenCalled();
});
it('should make a GET request with the error', async () => {
const mockResponse = new Response(null, { status: 503 });
(fetch as Mock).mockResolvedValueOnce(mockResponse);
await expect(ollamaService.getModels()).rejects.toThrow();
expect(global.fetch).toHaveBeenCalled();
});
});
});
+64
View File
@@ -0,0 +1,64 @@
import { ListResponse, Ollama as OllamaBrowser, ProgressResponse } from 'ollama/browser';
import { createErrorResponse } from '@/app/api/errorResponse';
import { ModelProvider } from '@/libs/agent-runtime';
import { useGlobalStore } from '@/store/global';
import { modelProviderSelectors } from '@/store/global/selectors';
import { ChatErrorType } from '@/types/fetch';
import { getMessageError } from '@/utils/fetch';
const DEFAULT_BASE_URL = 'http://127.0.0.1:11434/v1';
class OllamaService {
getHost = (): string => {
const endpoint = modelProviderSelectors.ollamaProxyUrl(useGlobalStore.getState());
const url = new URL(endpoint || DEFAULT_BASE_URL);
return url.host;
};
getOllamaClient = () => {
return new OllamaBrowser({ host: this.getHost() });
};
pullModel = async (model: string): Promise<AsyncGenerator<ProgressResponse>> => {
let response: Response | AsyncGenerator<ProgressResponse>;
try {
response = await this.getOllamaClient().pull({ insecure: true, model, stream: true });
return response;
} catch {
response = createErrorResponse(ChatErrorType.OllamaServiceUnavailable, {
host: this.getHost(),
message: 'please check whether your ollama service is available',
provider: ModelProvider.Ollama,
});
}
if (!response.ok) {
const messageError = await getMessageError(response);
throw messageError;
}
return response.json();
};
getModels = async (): Promise<ListResponse> => {
let response: Response | ListResponse;
try {
const response = await this.getOllamaClient().list();
return response;
} catch {
response = createErrorResponse(ChatErrorType.OllamaServiceUnavailable, {
host: this.getHost(),
message: 'please check whether your ollama service is available',
provider: ModelProvider.Ollama,
});
}
if (!response.ok) {
const messageError = await getMessageError(response);
throw messageError;
}
return response.json();
};
}
export const ollamaService = new OllamaService();
@@ -48,9 +48,6 @@ const mistralAPIKey = (s: GlobalStore) => modelProvider(s).mistral.apiKey;
const enableMoonshot = (s: GlobalStore) => modelProvider(s).moonshot.enabled;
const moonshotAPIKey = (s: GlobalStore) => modelProvider(s).moonshot.apiKey;
const enableOllamaConfigInSettings = (s: GlobalStore) =>
s.serverConfig.languageModel?.ollama?.enabled || false;
const enableOllama = (s: GlobalStore) => modelProvider(s).ollama.enabled;
const ollamaProxyUrl = (s: GlobalStore) => modelProvider(s).ollama.endpoint;
@@ -118,27 +115,32 @@ const processChatModels = (
};
const modelSelectList = (s: GlobalStore): ModelProviderCard[] => {
const string = [
const openaiModelString = [
s.serverConfig.customModelName,
currentSettings(s).languageModel.openAI.customModelName,
]
.filter(Boolean)
.join(',');
const modelConfig = parseModelString(string);
const openaiModelConfig = parseModelString(openaiModelString);
const chatModels = processChatModels(modelConfig);
const openaiChatModels = processChatModels(openaiModelConfig);
const ollamaModelConfig = parseModelString(
const ollamaModelString = [
s.serverConfig.languageModel?.ollama?.customModelName,
currentSettings(s).languageModel.ollama.customModelName,
);
]
.filter(Boolean)
.join(',');
const ollamaModelConfig = parseModelString(ollamaModelString);
const ollamaChatModels = processChatModels(ollamaModelConfig, OllamaProvider.chatModels);
return [
{
...OpenAIProvider,
chatModels,
chatModels: openaiChatModels,
},
// { ...azureModelList(s), enabled: enableAzure(s) },
{ ...ZhiPuProvider, enabled: enableZhipu(s) },
@@ -216,7 +218,6 @@ export const modelProviderSelectors = {
moonshotAPIKey,
// Ollama
enableOllamaConfigInSettings,
enableOllama,
ollamaProxyUrl,
@@ -1,5 +1,4 @@
import { PersistStorage } from 'zustand/middleware';
import { StorageValue } from 'zustand/middleware/persist';
import { PersistStorage, StorageValue } from 'zustand/middleware';
import { createIndexedDB } from './indexedDB';
import { createKeyMapper } from './keyMapper';
@@ -1,5 +1,5 @@
import { createStore, delMany, getMany, setMany } from 'idb-keyval';
import { StorageValue } from 'zustand/middleware/persist';
import { StorageValue } from 'zustand/middleware';
export const createIndexedDB = <State extends any>(dbName: string = 'indexedDB') => ({
getItem: async <T extends State>(name: string): Promise<StorageValue<T> | undefined> => {
@@ -1,4 +1,4 @@
import { StorageValue } from 'zustand/middleware/persist';
import { StorageValue } from 'zustand/middleware';
export const createLocalStorage = <State extends any>() => ({
getItem: <T extends State>(name: string): StorageValue<T> | undefined => {
@@ -1,5 +1,5 @@
import { isEmpty } from 'lodash-es';
import { StorageValue } from 'zustand/middleware/persist';
import { StorageValue } from 'zustand/middleware';
interface UrlSearchHelper {
getUrlSearch: () => string;
+1
View File
@@ -7,6 +7,7 @@ export const ChatErrorType = {
InvalidAccessCode: 'InvalidAccessCode', // 密码无效
OpenAIBizError: 'OpenAIBizError', // OpenAI 返回的业务错误
NoOpenAIAPIKey: 'NoOpenAIAPIKey',
OllamaServiceUnavailable: 'OllamaServiceUnavailable', // 未启动/检测到 Ollama 服务
// ******* 客户端错误 ******* //
BadRequest: 400,
+1 -1
View File
@@ -10,7 +10,7 @@
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",