mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-07 08:26:51 +00:00
feat: add LM Studio error messages and validation handling in embedder
This commit is contained in:
parent
cd240325ce
commit
2e24c8e7dc
20 changed files with 389 additions and 17 deletions
|
|
@ -17,6 +17,11 @@
|
|||
"modelNotEmbeddingCapable": "El model d'Ollama no és capaç de fer incrustacions: {{modelId}}",
|
||||
"hostNotFound": "No s'ha trobat l'amfitrió d'Ollama: {{baseUrl}}"
|
||||
},
|
||||
"lmstudio": {
|
||||
"serviceNotRunning": "El servidor de LM Studio no s'està executant a {{baseUrl}}. Assegureu-vos que LM Studio s'estigui executant amb el servidor local activat.",
|
||||
"modelNotFound": "No s'ha trobat el model \"{{modelId}}\" a LM Studio. Assegureu-vos que el model estigui carregat a LM Studio.",
|
||||
"hostNotFound": "No es pot connectar a LM Studio a {{baseUrl}}. Comproveu la configuració de l'URL base."
|
||||
},
|
||||
"scanner": {
|
||||
"unknownErrorProcessingFile": "Error desconegut en processar el fitxer {{filePath}}",
|
||||
"unknownErrorDeletingPoints": "Error desconegut en eliminar els punts per a {{filePath}}",
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@
|
|||
"modelNotEmbeddingCapable": "Ollama-Modell ist nicht für Einbettungen geeignet: {{modelId}}",
|
||||
"hostNotFound": "Ollama-Host nicht gefunden: {{baseUrl}}"
|
||||
},
|
||||
"lmstudio": {
|
||||
"serviceNotRunning": "Der LM Studio-Server wird unter {{baseUrl}} nicht ausgeführt. Bitte stelle sicher, dass LM Studio mit aktiviertem lokalen Server läuft.",
|
||||
"modelNotFound": "Modell \"{{modelId}}\" in LM Studio nicht gefunden. Bitte stelle sicher, dass das Modell in LM Studio geladen ist.",
|
||||
"hostNotFound": "Verbindung zu LM Studio unter {{baseUrl}} nicht möglich. Bitte überprüfe die Konfiguration der Basis-URL."
|
||||
},
|
||||
"scanner": {
|
||||
"unknownErrorProcessingFile": "Unbekannter Fehler beim Verarbeiten der Datei {{filePath}}",
|
||||
"unknownErrorDeletingPoints": "Unbekannter Fehler beim Löschen der Punkte für {{filePath}}",
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@
|
|||
"modelNotEmbeddingCapable": "Ollama model is not embedding capable: {{modelId}}",
|
||||
"hostNotFound": "Ollama host not found: {{baseUrl}}"
|
||||
},
|
||||
"lmstudio": {
|
||||
"serviceNotRunning": "LM Studio server is not running at {{baseUrl}}. Please ensure LM Studio is running with the local server enabled.",
|
||||
"modelNotFound": "Model \"{{modelId}}\" not found in LM Studio. Please ensure the model is loaded in LM Studio.",
|
||||
"hostNotFound": "Cannot connect to LM Studio at {{baseUrl}}. Please check the base URL configuration."
|
||||
},
|
||||
"scanner": {
|
||||
"unknownErrorProcessingFile": "Unknown error processing file {{filePath}}",
|
||||
"unknownErrorDeletingPoints": "Unknown error deleting points for {{filePath}}",
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@
|
|||
"modelNotEmbeddingCapable": "El modelo Ollama no es capaz de realizar incrustaciones: {{modelId}}",
|
||||
"hostNotFound": "No se encuentra el host de Ollama: {{baseUrl}}"
|
||||
},
|
||||
"lmstudio": {
|
||||
"serviceNotRunning": "El servidor de LM Studio no se está ejecutando en {{baseUrl}}. Asegúrate de que LM Studio se esté ejecutando con el servidor local habilitado.",
|
||||
"modelNotFound": "No se encontró el modelo \"{{modelId}}\" en LM Studio. Asegúrate de que el modelo esté cargado en LM Studio.",
|
||||
"hostNotFound": "No se puede conectar a LM Studio en {{baseUrl}}. Comprueba la configuración de la URL base."
|
||||
},
|
||||
"scanner": {
|
||||
"unknownErrorProcessingFile": "Error desconocido procesando archivo {{filePath}}",
|
||||
"unknownErrorDeletingPoints": "Error desconocido eliminando puntos para {{filePath}}",
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@
|
|||
"modelNotEmbeddingCapable": "Le modèle Ollama n'est pas capable d'intégrer : {{modelId}}",
|
||||
"hostNotFound": "Hôte Ollama introuvable : {{baseUrl}}"
|
||||
},
|
||||
"lmstudio": {
|
||||
"serviceNotRunning": "Le serveur LM Studio n'est pas en cours d'exécution à {{baseUrl}}. Veuillez vous assurer que LM Studio est en cours d'exécution avec le serveur local activé.",
|
||||
"modelNotFound": "Le modèle \"{{modelId}}\" n'a pas été trouvé dans LM Studio. Veuillez vous assurer que le modèle est chargé dans LM Studio.",
|
||||
"hostNotFound": "Impossible de se connecter à LM Studio à {{baseUrl}}. Veuillez vérifier la configuration de l'URL de base."
|
||||
},
|
||||
"scanner": {
|
||||
"unknownErrorProcessingFile": "Erreur inconnue lors du traitement du fichier {{filePath}}",
|
||||
"unknownErrorDeletingPoints": "Erreur inconnue lors de la suppression des points pour {{filePath}}",
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@
|
|||
"modelNotEmbeddingCapable": "ओलामा मॉडल एम्बेडिंग में सक्षम नहीं है: {{modelId}}",
|
||||
"hostNotFound": "ओलामा होस्ट नहीं मिला: {{baseUrl}}"
|
||||
},
|
||||
"lmstudio": {
|
||||
"serviceNotRunning": "एलएम स्टूडियो सर्वर {{baseUrl}} पर नहीं चल रहा है। कृपया सुनिश्चित करें कि एलएम स्टूडियो स्थानीय सर्वर सक्षम के साथ चल रहा है।",
|
||||
"modelNotFound": "एलएम स्टूडियो में मॉडल \"{{modelId}}\" नहीं मिला। कृपया सुनिश्चित करें कि मॉडल एलएम स्टूडियो में लोड किया गया है।",
|
||||
"hostNotFound": "{{baseUrl}} पर एलएम स्टूडियो से कनेक्ट नहीं हो सकता। कृपया आधार यूआरएल कॉन्फ़िगरेशन की जांच करें।"
|
||||
},
|
||||
"scanner": {
|
||||
"unknownErrorProcessingFile": "फ़ाइल {{filePath}} प्रसंस्करण में अज्ञात त्रुटि",
|
||||
"unknownErrorDeletingPoints": "{{filePath}} के लिए बिंदु हटाने में अज्ञात त्रुटि",
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@
|
|||
"modelNotEmbeddingCapable": "Model Ollama tidak mampu melakukan embedding: {{modelId}}",
|
||||
"hostNotFound": "Host Ollama tidak ditemukan: {{baseUrl}}"
|
||||
},
|
||||
"lmstudio": {
|
||||
"serviceNotRunning": "Server LM Studio tidak berjalan di {{baseUrl}}. Pastikan LM Studio berjalan dengan server lokal diaktifkan.",
|
||||
"modelNotFound": "Model \"{{modelId}}\" tidak ditemukan di LM Studio. Pastikan model dimuat di LM Studio.",
|
||||
"hostNotFound": "Tidak dapat terhubung ke LM Studio di {{baseUrl}}. Silakan periksa konfigurasi URL dasar."
|
||||
},
|
||||
"scanner": {
|
||||
"unknownErrorProcessingFile": "Error tidak dikenal saat memproses file {{filePath}}",
|
||||
"unknownErrorDeletingPoints": "Error tidak dikenal saat menghapus points untuk {{filePath}}",
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@
|
|||
"modelNotEmbeddingCapable": "Il modello Ollama non è in grado di eseguire l'embedding: {{modelId}}",
|
||||
"hostNotFound": "Host Ollama non trovato: {{baseUrl}}"
|
||||
},
|
||||
"lmstudio": {
|
||||
"serviceNotRunning": "Il server di LM Studio non è in esecuzione su {{baseUrl}}. Assicurati che LM Studio sia in esecuzione con il server locale abilitato.",
|
||||
"modelNotFound": "Modello \"{{modelId}}\" non trovato in LM Studio. Assicurati che il modello sia caricato in LM Studio.",
|
||||
"hostNotFound": "Impossibile connettersi a LM Studio su {{baseUrl}}. Controlla la configurazione dell'URL di base."
|
||||
},
|
||||
"scanner": {
|
||||
"unknownErrorProcessingFile": "Errore sconosciuto nell'elaborazione del file {{filePath}}",
|
||||
"unknownErrorDeletingPoints": "Errore sconosciuto nell'eliminazione dei punti per {{filePath}}",
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@
|
|||
"modelNotEmbeddingCapable": "Ollamaモデルは埋め込みに対応していません:{{modelId}}",
|
||||
"hostNotFound": "Ollamaホストが見つかりません:{{baseUrl}}"
|
||||
},
|
||||
"lmstudio": {
|
||||
"serviceNotRunning": "LM Studioサーバーが{{baseUrl}}で実行されていません。LM Studioがローカルサーバーを有効にして実行されていることを確認してください。",
|
||||
"modelNotFound": "モデル「{{modelId}}」がLM Studioで見つかりません。モデルがLM Studioにロードされていることを確認してください。",
|
||||
"hostNotFound": "{{baseUrl}}のLM Studioに接続できません。ベースURLの構成を確認してください。"
|
||||
},
|
||||
"scanner": {
|
||||
"unknownErrorProcessingFile": "ファイル{{filePath}}の処理中に不明なエラーが発生しました",
|
||||
"unknownErrorDeletingPoints": "{{filePath}}のポイント削除中に不明なエラーが発生しました",
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@
|
|||
"modelNotEmbeddingCapable": "Ollama 모델은 임베딩이 불가능합니다: {{modelId}}",
|
||||
"hostNotFound": "Ollama 호스트를 찾을 수 없습니다: {{baseUrl}}"
|
||||
},
|
||||
"lmstudio": {
|
||||
"serviceNotRunning": "LM Studio 서버가 {{baseUrl}}에서 실행되고 있지 않습니다. 로컬 서버가 활성화된 상태로 LM Studio가 실행 중인지 확인하세요.",
|
||||
"modelNotFound": "LM Studio에서 \"{{modelId}}\" 모델을 찾을 수 없습니다. 모델이 LM Studio에 로드되었는지 확인하세요.",
|
||||
"hostNotFound": "{{baseUrl}}에서 LM Studio에 연결할 수 없습니다. 기본 URL 구성을 확인하세요."
|
||||
},
|
||||
"scanner": {
|
||||
"unknownErrorProcessingFile": "파일 {{filePath}} 처리 중 알 수 없는 오류",
|
||||
"unknownErrorDeletingPoints": "{{filePath}}의 포인트 삭제 중 알 수 없는 오류",
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@
|
|||
"modelNotEmbeddingCapable": "Ollama-model is niet in staat tot insluiten: {{modelId}}",
|
||||
"hostNotFound": "Ollama-host niet gevonden: {{baseUrl}}"
|
||||
},
|
||||
"lmstudio": {
|
||||
"serviceNotRunning": "LM Studio-server draait niet op {{baseUrl}}. Zorg ervoor dat LM Studio draait met de lokale server ingeschakeld.",
|
||||
"modelNotFound": "Model \"{{modelId}}\" niet gevonden in LM Studio. Zorg ervoor dat het model in LM Studio is geladen.",
|
||||
"hostNotFound": "Kan geen verbinding maken met LM Studio op {{baseUrl}}. Controleer de basis-URL-configuratie."
|
||||
},
|
||||
"scanner": {
|
||||
"unknownErrorProcessingFile": "Onbekende fout bij verwerken van bestand {{filePath}}",
|
||||
"unknownErrorDeletingPoints": "Onbekende fout bij verwijderen van punten voor {{filePath}}",
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@
|
|||
"modelNotEmbeddingCapable": "Model Ollama nie jest zdolny do osadzania: {{modelId}}",
|
||||
"hostNotFound": "Nie znaleziono hosta Ollama: {{baseUrl}}"
|
||||
},
|
||||
"lmstudio": {
|
||||
"serviceNotRunning": "Serwer LM Studio nie działa pod adresem {{baseUrl}}. Upewnij się, że LM Studio jest uruchomione z włączonym serwerem lokalnym.",
|
||||
"modelNotFound": "Nie znaleziono modelu \"{{modelId}}\" w LM Studio. Upewnij się, że model jest załadowany w LM Studio.",
|
||||
"hostNotFound": "Nie można połączyć się z LM Studio pod adresem {{baseUrl}}. Sprawdź konfigurację podstawowego adresu URL."
|
||||
},
|
||||
"scanner": {
|
||||
"unknownErrorProcessingFile": "Nieznany błąd podczas przetwarzania pliku {{filePath}}",
|
||||
"unknownErrorDeletingPoints": "Nieznany błąd podczas usuwania punktów dla {{filePath}}",
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@
|
|||
"modelNotEmbeddingCapable": "O modelo Ollama não é capaz de embedding: {{modelId}}",
|
||||
"hostNotFound": "Host Ollama não encontrado: {{baseUrl}}"
|
||||
},
|
||||
"lmstudio": {
|
||||
"serviceNotRunning": "O servidor do LM Studio não está em execução em {{baseUrl}}. Certifique-se de que o LM Studio esteja em execução com o servidor local ativado.",
|
||||
"modelNotFound": "Modelo \"{{modelId}}\" não encontrado no LM Studio. Certifique-se de que o modelo esteja carregado no LM Studio.",
|
||||
"hostNotFound": "Não é possível conectar-se ao LM Studio em {{baseUrl}}. Verifique a configuração do URL base."
|
||||
},
|
||||
"scanner": {
|
||||
"unknownErrorProcessingFile": "Erro desconhecido ao processar arquivo {{filePath}}",
|
||||
"unknownErrorDeletingPoints": "Erro desconhecido ao deletar pontos para {{filePath}}",
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@
|
|||
"modelNotEmbeddingCapable": "Модель Ollama не способна к вложению: {{modelId}}",
|
||||
"hostNotFound": "Хост Ollama не найден: {{baseUrl}}"
|
||||
},
|
||||
"lmstudio": {
|
||||
"serviceNotRunning": "Сервер LM Studio не запущен по адресу {{baseUrl}}. Убедитесь, что LM Studio запущен с включенным локальным сервером.",
|
||||
"modelNotFound": "Модель \"{{modelId}}\" не найдена в LM Studio. Убедитесь, что модель загружена в LM Studio.",
|
||||
"hostNotFound": "Не удается подключиться к LM Studio по адресу {{baseUrl}}. Проверьте конфигурацию базового URL-адреса."
|
||||
},
|
||||
"scanner": {
|
||||
"unknownErrorProcessingFile": "Неизвестная ошибка при обработке файла {{filePath}}",
|
||||
"unknownErrorDeletingPoints": "Неизвестная ошибка при удалении точек для {{filePath}}",
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@
|
|||
"modelNotEmbeddingCapable": "Ollama modeli gömme yeteneğine sahip değil: {{modelId}}",
|
||||
"hostNotFound": "Ollama ana bilgisayarı bulunamadı: {{baseUrl}}"
|
||||
},
|
||||
"lmstudio": {
|
||||
"serviceNotRunning": "LM Studio sunucusu {{baseUrl}} adresinde çalışmıyor. Lütfen LM Studio'nun yerel sunucu etkinken çalıştığından emin olun.",
|
||||
"modelNotFound": "LM Studio'da \"{{modelId}}\" modeli bulunamadı. Lütfen modelin LM Studio'da yüklü olduğundan emin olun.",
|
||||
"hostNotFound": "LM Studio'ya {{baseUrl}} adresinden bağlanılamıyor. Lütfen temel URL yapılandırmasını kontrol edin."
|
||||
},
|
||||
"scanner": {
|
||||
"unknownErrorProcessingFile": "{{filePath}} dosyası işlenirken bilinmeyen hata",
|
||||
"unknownErrorDeletingPoints": "{{filePath}} için noktalar silinirken bilinmeyen hata",
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@
|
|||
"modelNotEmbeddingCapable": "Mô hình Ollama không có khả năng nhúng: {{modelId}}",
|
||||
"hostNotFound": "Không tìm thấy máy chủ Ollama: {{baseUrl}}"
|
||||
},
|
||||
"lmstudio": {
|
||||
"serviceNotRunning": "Máy chủ LM Studio không chạy tại {{baseUrl}}. Vui lòng đảm bảo LM Studio đang chạy với máy chủ cục bộ được bật.",
|
||||
"modelNotFound": "Không tìm thấy mô hình \"{{modelId}}\" trong LM Studio. Vui lòng đảm bảo mô hình đã được tải trong LM Studio.",
|
||||
"hostNotFound": "Không thể kết nối với LM Studio tại {{baseUrl}}. Vui lòng kiểm tra cấu hình URL cơ sở."
|
||||
},
|
||||
"scanner": {
|
||||
"unknownErrorProcessingFile": "Lỗi không xác định khi xử lý tệp {{filePath}}",
|
||||
"unknownErrorDeletingPoints": "Lỗi không xác định khi xóa điểm cho {{filePath}}",
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@
|
|||
"modelNotEmbeddingCapable": "Ollama 模型不具备嵌入能力:{{modelId}}",
|
||||
"hostNotFound": "未找到 Ollama 主机:{{baseUrl}}"
|
||||
},
|
||||
"lmstudio": {
|
||||
"serviceNotRunning": "LM Studio 服务器未在 {{baseUrl}} 运行。请确保 LM Studio 已启用本地服务器并正在运行。",
|
||||
"modelNotFound": "在 LM Studio 中未找到模型“{{modelId}}”。请确保该模型已在 LM Studio 中加载。",
|
||||
"hostNotFound": "无法连接到 {{baseUrl}} 上的 LM Studio。请检查基本 URL 配置。"
|
||||
},
|
||||
"scanner": {
|
||||
"unknownErrorProcessingFile": "处理文件 {{filePath}} 时出现未知错误",
|
||||
"unknownErrorDeletingPoints": "删除 {{filePath}} 的数据点时出现未知错误",
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@
|
|||
"modelNotEmbeddingCapable": "Ollama 模型不具備內嵌能力:{{modelId}}",
|
||||
"hostNotFound": "找不到 Ollama 主機:{{baseUrl}}"
|
||||
},
|
||||
"lmstudio": {
|
||||
"serviceNotRunning": "LM Studio 伺服器未在 {{baseUrl}} 執行。請確保 LM Studio 已啟用本機伺服器並正在執行。",
|
||||
"modelNotFound": "在 LM Studio 中找不到模型「{{modelId}}」。請確保該模型已在 LM Studio 中載入。",
|
||||
"hostNotFound": "無法連線至 {{baseUrl}} 上的 LM Studio。請檢查基礎 URL 設定。"
|
||||
},
|
||||
"scanner": {
|
||||
"unknownErrorProcessingFile": "處理檔案 {{filePath}} 時發生未知錯誤",
|
||||
"unknownErrorDeletingPoints": "刪除 {{filePath}} 的資料點時發生未知錯誤",
|
||||
|
|
|
|||
210
src/services/code-index/embedders/__tests__/lmstudio.spec.ts
Normal file
210
src/services/code-index/embedders/__tests__/lmstudio.spec.ts
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
import { vitest, describe, it, expect, beforeEach, afterEach } from "vitest"
|
||||
import type { MockedFunction } from "vitest"
|
||||
import { CodeIndexLmStudioEmbedder } from "../lmstudio"
|
||||
import { OpenAI } from "openai"
|
||||
|
||||
vitest.mock("openai", () => {
|
||||
const mockEmbeddingsCreate = vitest.fn()
|
||||
return {
|
||||
OpenAI: vitest.fn().mockImplementation(() => ({
|
||||
embeddings: {
|
||||
create: mockEmbeddingsCreate,
|
||||
},
|
||||
})),
|
||||
}
|
||||
})
|
||||
|
||||
const consoleMocks = {
|
||||
error: vitest.spyOn(console, "error").mockImplementation(() => {}),
|
||||
warn: vitest.spyOn(console, "warn").mockImplementation(() => {}),
|
||||
}
|
||||
|
||||
describe("CodeIndexLmStudioEmbedder", () => {
|
||||
let embedder: CodeIndexLmStudioEmbedder
|
||||
let mockEmbeddingsCreate: MockedFunction<any>
|
||||
|
||||
beforeEach(() => {
|
||||
vitest.clearAllMocks()
|
||||
consoleMocks.error.mockClear()
|
||||
consoleMocks.warn.mockClear()
|
||||
|
||||
const MockedOpenAI = OpenAI as any
|
||||
mockEmbeddingsCreate = vitest.fn()
|
||||
MockedOpenAI.mockImplementation(() => ({
|
||||
embeddings: {
|
||||
create: mockEmbeddingsCreate,
|
||||
},
|
||||
}))
|
||||
|
||||
embedder = new CodeIndexLmStudioEmbedder({
|
||||
lmStudioBaseUrl: "http://localhost:1234",
|
||||
embeddingModelId: "text-embedding-nomic-embed-text-v1.5@f16",
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
vitest.clearAllMocks()
|
||||
})
|
||||
|
||||
describe("constructor", () => {
|
||||
it("should initialize with provided options", () => {
|
||||
expect(embedder.embedderInfo.name).toBe("lmstudio")
|
||||
})
|
||||
|
||||
it("should use default values when not provided", () => {
|
||||
const embedderWithDefaults = new CodeIndexLmStudioEmbedder({})
|
||||
expect(embedderWithDefaults.embedderInfo.name).toBe("lmstudio")
|
||||
})
|
||||
|
||||
it("should normalize base URL to include /v1", () => {
|
||||
const embedderWithoutV1 = new CodeIndexLmStudioEmbedder({
|
||||
lmStudioBaseUrl: "http://localhost:1234",
|
||||
})
|
||||
expect(embedderWithoutV1.embedderInfo.name).toBe("lmstudio")
|
||||
|
||||
const embedderWithV1 = new CodeIndexLmStudioEmbedder({
|
||||
lmStudioBaseUrl: "http://localhost:1234/v1",
|
||||
})
|
||||
expect(embedderWithV1.embedderInfo.name).toBe("lmstudio")
|
||||
})
|
||||
})
|
||||
|
||||
describe("validateConfiguration", () => {
|
||||
it("should validate successfully with valid configuration", async () => {
|
||||
const mockResponse = {
|
||||
data: [{ embedding: [0.1, 0.2, 0.3] }],
|
||||
usage: { prompt_tokens: 2, total_tokens: 2 },
|
||||
}
|
||||
mockEmbeddingsCreate.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await embedder.validateConfiguration()
|
||||
|
||||
expect(result.valid).toBe(true)
|
||||
expect(result.error).toBeUndefined()
|
||||
expect(mockEmbeddingsCreate).toHaveBeenCalledWith({
|
||||
input: ["test"],
|
||||
model: "text-embedding-nomic-embed-text-v1.5@f16",
|
||||
encoding_format: "float",
|
||||
})
|
||||
})
|
||||
|
||||
it("should fail validation when response has no data", async () => {
|
||||
const mockResponse = {
|
||||
data: [],
|
||||
usage: { prompt_tokens: 0, total_tokens: 0 },
|
||||
}
|
||||
mockEmbeddingsCreate.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await embedder.validateConfiguration()
|
||||
|
||||
expect(result.valid).toBe(false)
|
||||
expect(result.error).toBe("embeddings:validation.invalidResponse")
|
||||
})
|
||||
|
||||
it("should fail validation when LM Studio is not running (ECONNREFUSED)", async () => {
|
||||
const error = new Error("ECONNREFUSED")
|
||||
;(error as any).code = "ECONNREFUSED"
|
||||
mockEmbeddingsCreate.mockRejectedValue(error)
|
||||
|
||||
const result = await embedder.validateConfiguration()
|
||||
|
||||
expect(result.valid).toBe(false)
|
||||
expect(result.error).toBe("lmstudio.serviceNotRunning")
|
||||
})
|
||||
|
||||
it("should fail validation when model is not found (404)", async () => {
|
||||
const error = new Error("HTTP 404: Not Found")
|
||||
;(error as any).status = 404
|
||||
mockEmbeddingsCreate.mockRejectedValue(error)
|
||||
|
||||
const result = await embedder.validateConfiguration()
|
||||
|
||||
expect(result.valid).toBe(false)
|
||||
expect(result.error).toBe("lmstudio.modelNotFound")
|
||||
})
|
||||
|
||||
it("should fail validation when host is not found (ENOTFOUND)", async () => {
|
||||
const error = new Error("ENOTFOUND")
|
||||
;(error as any).code = "ENOTFOUND"
|
||||
mockEmbeddingsCreate.mockRejectedValue(error)
|
||||
|
||||
const result = await embedder.validateConfiguration()
|
||||
|
||||
expect(result.valid).toBe(false)
|
||||
expect(result.error).toBe("lmstudio.hostNotFound")
|
||||
})
|
||||
|
||||
it("should handle generic errors with standard error handling", async () => {
|
||||
const error = new Error("Unknown error")
|
||||
mockEmbeddingsCreate.mockRejectedValue(error)
|
||||
|
||||
const result = await embedder.validateConfiguration()
|
||||
|
||||
expect(result.valid).toBe(false)
|
||||
|
||||
expect(result.error).toBe("embeddings:validation.configurationError")
|
||||
})
|
||||
|
||||
it("should handle fetch failed errors", async () => {
|
||||
const error = new Error("fetch failed")
|
||||
mockEmbeddingsCreate.mockRejectedValue(error)
|
||||
|
||||
const result = await embedder.validateConfiguration()
|
||||
|
||||
expect(result.valid).toBe(false)
|
||||
expect(result.error).toBe("lmstudio.serviceNotRunning")
|
||||
})
|
||||
})
|
||||
|
||||
describe("createEmbeddings", () => {
|
||||
it("should create embeddings successfully", async () => {
|
||||
const mockResponse = {
|
||||
data: [{ embedding: [0.1, 0.2, 0.3] }, { embedding: [0.4, 0.5, 0.6] }],
|
||||
usage: { prompt_tokens: 10, total_tokens: 10 },
|
||||
}
|
||||
mockEmbeddingsCreate.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await embedder.createEmbeddings(["test1", "test2"])
|
||||
|
||||
expect(result.embeddings).toEqual([
|
||||
[0.1, 0.2, 0.3],
|
||||
[0.4, 0.5, 0.6],
|
||||
])
|
||||
expect(result.usage).toEqual({ promptTokens: 10, totalTokens: 10 })
|
||||
})
|
||||
|
||||
it("should handle rate limit errors with retry", async () => {
|
||||
const error = new Error("Rate limit exceeded")
|
||||
;(error as any).status = 429
|
||||
|
||||
mockEmbeddingsCreate.mockRejectedValueOnce(error)
|
||||
|
||||
const mockResponse = {
|
||||
data: [{ embedding: [0.1, 0.2, 0.3] }],
|
||||
usage: { prompt_tokens: 5, total_tokens: 5 },
|
||||
}
|
||||
mockEmbeddingsCreate.mockResolvedValueOnce(mockResponse)
|
||||
|
||||
const result = await embedder.createEmbeddings(["test"])
|
||||
|
||||
expect(result.embeddings).toEqual([[0.1, 0.2, 0.3]])
|
||||
expect(mockEmbeddingsCreate).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it("should skip texts that exceed token limit", async () => {
|
||||
const longText = "a".repeat(100000)
|
||||
const shortText = "short text"
|
||||
|
||||
const mockResponse = {
|
||||
data: [{ embedding: [0.1, 0.2, 0.3] }],
|
||||
usage: { prompt_tokens: 3, total_tokens: 3 },
|
||||
}
|
||||
mockEmbeddingsCreate.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await embedder.createEmbeddings([longText, shortText])
|
||||
|
||||
expect(result.embeddings).toEqual([[0.1, 0.2, 0.3]])
|
||||
expect(consoleMocks.warn).toHaveBeenCalledWith(expect.stringContaining("exceeds maximum token limit"))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -7,6 +7,8 @@ import {
|
|||
MAX_BATCH_RETRIES as MAX_RETRIES,
|
||||
INITIAL_RETRY_DELAY_MS as INITIAL_DELAY_MS,
|
||||
} from "../constants"
|
||||
import { withValidationErrorHandling, formatEmbeddingError, HttpError } from "../shared/validation-helpers"
|
||||
import { t } from "../../../i18n"
|
||||
|
||||
/**
|
||||
* LM Studio implementation of the embedder interface with batching and rate limiting.
|
||||
|
|
@ -81,19 +83,10 @@ export class CodeIndexLmStudioEmbedder implements IEmbedder {
|
|||
}
|
||||
|
||||
if (currentBatch.length > 0) {
|
||||
try {
|
||||
const batchResult = await this._embedBatchWithRetries(currentBatch, modelToUse)
|
||||
|
||||
allEmbeddings.push(...batchResult.embeddings)
|
||||
usage.promptTokens += batchResult.usage.promptTokens
|
||||
usage.totalTokens += batchResult.usage.totalTokens
|
||||
} catch (error) {
|
||||
const batchInfo = `batch of ${currentBatch.length} documents (indices: ${processedIndices.join(", ")})`
|
||||
console.error(`Failed to process ${batchInfo}:`, error)
|
||||
throw new Error(
|
||||
`Failed to create embeddings for ${batchInfo}: ${error instanceof Error ? error.message : "batch processing error"}`,
|
||||
)
|
||||
}
|
||||
const batchResult = await this._embedBatchWithRetries(currentBatch, modelToUse)
|
||||
allEmbeddings.push(...batchResult.embeddings)
|
||||
usage.promptTokens += batchResult.usage.promptTokens
|
||||
usage.totalTokens += batchResult.usage.totalTokens
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -115,7 +108,6 @@ export class CodeIndexLmStudioEmbedder implements IEmbedder {
|
|||
const response = await this.embeddingsClient.embeddings.create({
|
||||
input: batchTexts,
|
||||
model: model,
|
||||
encoding_format: "float",
|
||||
})
|
||||
|
||||
return {
|
||||
|
|
@ -126,10 +118,11 @@ export class CodeIndexLmStudioEmbedder implements IEmbedder {
|
|||
},
|
||||
}
|
||||
} catch (error: any) {
|
||||
const isRateLimitError = error?.status === 429
|
||||
const hasMoreAttempts = attempts < MAX_RETRIES - 1
|
||||
|
||||
if (isRateLimitError && hasMoreAttempts) {
|
||||
// Check if it's a rate limit error
|
||||
const httpError = error as HttpError
|
||||
if (httpError?.status === 429 && hasMoreAttempts) {
|
||||
const delayMs = INITIAL_DELAY_MS * Math.pow(2, attempts)
|
||||
await new Promise((resolve) => setTimeout(resolve, delayMs))
|
||||
continue
|
||||
|
|
@ -139,7 +132,86 @@ export class CodeIndexLmStudioEmbedder implements IEmbedder {
|
|||
}
|
||||
}
|
||||
|
||||
throw new Error(`Failed to create embeddings after ${MAX_RETRIES} attempts`)
|
||||
throw new Error(t("embeddings:failedMaxAttempts", { attempts: MAX_RETRIES }))
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the LM Studio embedder configuration by testing connectivity and model availability
|
||||
* @returns Promise resolving to validation result with success status and optional error message
|
||||
*/
|
||||
async validateConfiguration(): Promise<{ valid: boolean; error?: string }> {
|
||||
return withValidationErrorHandling(
|
||||
async () => {
|
||||
// Test with a minimal embedding request
|
||||
const testTexts = ["test"]
|
||||
const modelToUse = this.defaultModelId
|
||||
|
||||
try {
|
||||
const response = await this.embeddingsClient.embeddings.create({
|
||||
input: testTexts,
|
||||
model: modelToUse,
|
||||
})
|
||||
|
||||
// Check if we got a valid response
|
||||
if (!response.data || response.data.length === 0) {
|
||||
return {
|
||||
valid: false,
|
||||
error: t("embeddings:validation.invalidResponse"),
|
||||
}
|
||||
}
|
||||
|
||||
return { valid: true }
|
||||
} catch (error: any) {
|
||||
// Handle LM Studio specific errors
|
||||
if (error?.message?.includes("ECONNREFUSED") || error?.code === "ECONNREFUSED") {
|
||||
return {
|
||||
valid: false,
|
||||
error: t("embeddings:lmstudio.serviceNotRunning", {
|
||||
baseUrl: this.options.lmStudioBaseUrl,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
if (error?.status === 404 || error?.message?.includes("404")) {
|
||||
return {
|
||||
valid: false,
|
||||
error: t("embeddings:lmstudio.modelNotFound", { modelId: modelToUse }),
|
||||
}
|
||||
}
|
||||
|
||||
// Re-throw to let standard error handling take over
|
||||
throw error
|
||||
}
|
||||
},
|
||||
"lmstudio",
|
||||
{
|
||||
beforeStandardHandling: (error: any) => {
|
||||
// Handle LM Studio-specific connection errors
|
||||
if (
|
||||
error?.message?.includes("fetch failed") ||
|
||||
error?.code === "ECONNREFUSED" ||
|
||||
error?.message?.includes("ECONNREFUSED")
|
||||
) {
|
||||
return {
|
||||
valid: false,
|
||||
error: t("embeddings:lmstudio.serviceNotRunning", {
|
||||
baseUrl: this.options.lmStudioBaseUrl,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
if (error?.code === "ENOTFOUND" || error?.message?.includes("ENOTFOUND")) {
|
||||
return {
|
||||
valid: false,
|
||||
error: t("embeddings:lmstudio.hostNotFound", { baseUrl: this.options.lmStudioBaseUrl }),
|
||||
}
|
||||
}
|
||||
|
||||
// Let standard handling take over
|
||||
return undefined
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
get embedderInfo(): EmbedderInfo {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue