Compare commits
1 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
1cb82f4216 |
21
.env.example
21
.env.example
|
|
@ -30,9 +30,26 @@ CHROMA_SERVER_PORT=8002
|
|||
CHROMA_COLLECTION_NAME=rag_collection
|
||||
|
||||
# ============================================
|
||||
# Ollama 配置
|
||||
# LLM 配置 (用于文本生成)
|
||||
# ============================================
|
||||
# LLM provider: ollama, openai 等
|
||||
LLM_PROVIDER=ollama
|
||||
LLM_BASE_URL=http://localhost:11434/v1
|
||||
LLM_MODEL=qwen3:8b
|
||||
LLM_API_KEY= # 如使用openai等需要API Key的服务
|
||||
|
||||
# ============================================
|
||||
# Embedding 配置 (用于向量检索)
|
||||
# ============================================
|
||||
# Embedding provider: ollama, openai 等
|
||||
EMBEDDING_PROVIDER=ollama
|
||||
EMBEDDING_BASE_URL=http://localhost:11434
|
||||
EMBEDDING_MODEL=qwen3-embedding:0.6b
|
||||
EMBEDDING_API_KEY= # 如使用openai等需要API Key的服务
|
||||
|
||||
# ============================================
|
||||
# 兼容旧版本配置 (已废弃,仍可用但推荐使用上面的配置)
|
||||
# ============================================
|
||||
# OLLAMA_BASE_URL: Ollama 服务地址
|
||||
OLLAMA_BASE_URL=http://localhost:11434
|
||||
OLLAMA_MODEL=qwen3:8b
|
||||
OLLAMA_EMBEDDING_MODEL=qwen3-embedding:0.6b
|
||||
|
|
|
|||
88
config.py
88
config.py
|
|
@ -138,13 +138,24 @@ class Settings(BaseSettings):
|
|||
CHROMA_DB_PATH: str = "./chroma_db" # Only used for PersistentClient mode
|
||||
CHROMA_COLLECTION_NAME: str = "rag_collection"
|
||||
|
||||
# Ollama Settings
|
||||
# Configure OLLAMA_BASE_URL in .env file based on your deployment
|
||||
# - Local: http://localhost:11434
|
||||
# - Remote: http://192.168.1.100:11434
|
||||
# LLM Settings
|
||||
# LLM provider: "ollama", "vllm", "openai", "deepseek" etc.
|
||||
LLM_PROVIDER: str = "ollama"
|
||||
LLM_BASE_URL: str = "http://localhost:11434"
|
||||
LLM_MODEL: str = "qwen3:8b"
|
||||
LLM_API_KEY: Optional[str] = None
|
||||
|
||||
# Embedding Settings
|
||||
# Embedding provider: "ollama", "vllm", "openai" etc.
|
||||
EMBEDDING_PROVIDER: str = "ollama"
|
||||
EMBEDDING_BASE_URL: str = "http://localhost:11434"
|
||||
EMBEDDING_MODEL: str = "qwen3-embedding:0.6b"
|
||||
EMBEDDING_API_KEY: Optional[str] = None
|
||||
|
||||
# Legacy Ollama Settings (for backward compatibility)
|
||||
OLLAMA_BASE_URL: str = "http://localhost:11434"
|
||||
OLLAMA_MODEL: str = "qwen3:1.7b" # LLM model for text generation
|
||||
OLLAMA_EMBEDDING_MODEL: str = "qwen3-embedding:0.6b" # Embedding model for vectorization
|
||||
OLLAMA_MODEL: str = "qwen3:8b"
|
||||
OLLAMA_EMBEDDING_MODEL: str = "qwen3-embedding:0.6b"
|
||||
|
||||
# RAG Settings
|
||||
EMBEDDING_DIMENSION: int = 768
|
||||
|
|
@ -184,6 +195,71 @@ class Settings(BaseSettings):
|
|||
extra="ignore" # Ignore extra fields in .env file that are not defined in Settings
|
||||
)
|
||||
|
||||
def get_llm_config(self) -> Dict[str, Any]:
|
||||
"""
|
||||
Get LLM configuration with backward compatibility for legacy OLLAMA_* settings.
|
||||
|
||||
Returns:
|
||||
Dictionary with provider, base_url, model, and api_key
|
||||
"""
|
||||
if self.LLM_PROVIDER == "ollama":
|
||||
return {
|
||||
"provider": "ollama",
|
||||
"base_url": self.LLM_BASE_URL or self.OLLAMA_BASE_URL,
|
||||
"model": self.LLM_MODEL or self.OLLAMA_MODEL,
|
||||
"api_key": self.LLM_API_KEY
|
||||
}
|
||||
elif self.LLM_PROVIDER == "openai":
|
||||
return {
|
||||
"provider": "openai",
|
||||
"base_url": self.LLM_BASE_URL or "https://api.openai.com/v1",
|
||||
"model": self.LLM_MODEL,
|
||||
"api_key": self.LLM_API_KEY
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"provider": self.LLM_PROVIDER,
|
||||
"base_url": self.LLM_BASE_URL,
|
||||
"model": self.LLM_MODEL,
|
||||
"api_key": self.LLM_API_KEY
|
||||
}
|
||||
|
||||
def get_embedding_config(self) -> Dict[str, Any]:
|
||||
"""
|
||||
Get Embedding configuration with backward compatibility for legacy OLLAMA_* settings.
|
||||
|
||||
Returns:
|
||||
Dictionary with provider, base_url, model, and api_key
|
||||
"""
|
||||
if self.EMBEDDING_PROVIDER == "ollama":
|
||||
return {
|
||||
"provider": "ollama",
|
||||
"base_url": self.EMBEDDING_BASE_URL or self.OLLAMA_BASE_URL,
|
||||
"model": self.EMBEDDING_MODEL or self.OLLAMA_EMBEDDING_MODEL,
|
||||
"api_key": self.EMBEDDING_API_KEY
|
||||
}
|
||||
elif self.EMBEDDING_PROVIDER == "vllm":
|
||||
return {
|
||||
"provider": "vllm",
|
||||
"base_url": self.EMBEDDING_BASE_URL,
|
||||
"model": self.EMBEDDING_MODEL,
|
||||
"api_key": self.EMBEDDING_API_KEY
|
||||
}
|
||||
elif self.EMBEDDING_PROVIDER == "openai":
|
||||
return {
|
||||
"provider": "openai",
|
||||
"base_url": self.EMBEDDING_BASE_URL or "https://api.openai.com/v1",
|
||||
"model": self.EMBEDDING_MODEL,
|
||||
"api_key": self.EMBEDDING_API_KEY
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"provider": self.EMBEDDING_PROVIDER,
|
||||
"base_url": self.EMBEDDING_BASE_URL,
|
||||
"model": self.EMBEDDING_MODEL,
|
||||
"api_key": self.EMBEDDING_API_KEY
|
||||
}
|
||||
|
||||
def get_data_sources(self) -> List[BaseDataSourceConfig]:
|
||||
"""
|
||||
Get list of data source configurations
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ from llama_index.core.query_engine import RetrieverQueryEngine
|
|||
from llama_index.core.response_synthesizers import ResponseMode
|
||||
from llama_index.core.base.response.schema import StreamingResponse
|
||||
from llama_index.llms.ollama import Ollama
|
||||
from llama_index.llms.openai import OpenAI
|
||||
from llama_index.core import PromptTemplate
|
||||
from typing import AsyncIterator, Optional, Tuple
|
||||
import asyncio
|
||||
|
|
@ -82,42 +83,71 @@ class RAGEngine:
|
|||
"""Main RAG engine for query processing"""
|
||||
|
||||
@staticmethod
|
||||
def check_ollama_connection() -> Tuple[bool, str]:
|
||||
def check_llm_connection(provider: str, base_url: str, model: str, api_key: Optional[str] = None) -> Tuple[bool, str]:
|
||||
"""
|
||||
Check if Ollama server is accessible and connection can be established
|
||||
Check if LLM server is accessible and connection can be established
|
||||
|
||||
Args:
|
||||
provider: LLM provider (ollama, vllm, openai)
|
||||
base_url: Base URL for the LLM service
|
||||
model: Model name
|
||||
api_key: Optional API key for authentication
|
||||
|
||||
Returns:
|
||||
Tuple of (is_connected: bool, error_message: str)
|
||||
If connected, error_message will be empty string
|
||||
"""
|
||||
try:
|
||||
logger.info(f"Checking Ollama connection to {settings.OLLAMA_BASE_URL}...")
|
||||
headers = {}
|
||||
if api_key:
|
||||
headers["Authorization"] = f"Bearer {api_key}"
|
||||
|
||||
# Test connection by calling Ollama API
|
||||
with httpx.Client(timeout=10.0) as client:
|
||||
response = client.get(f"{settings.OLLAMA_BASE_URL}/api/tags")
|
||||
if response.status_code == 200:
|
||||
models = response.json().get("models", [])
|
||||
model_names = [m.get("name", "unknown") for m in models]
|
||||
logger.info(f"✓ Ollama connection successful (found {len(models)} model(s): {', '.join(model_names[:3])}{'...' if len(model_names) > 3 else ''})")
|
||||
if provider == "ollama":
|
||||
logger.info(f"Checking Ollama connection to {base_url}...")
|
||||
with httpx.Client(timeout=10.0) as client:
|
||||
response = client.get(f"{base_url}/api/tags")
|
||||
if response.status_code == 200:
|
||||
models = response.json().get("models", [])
|
||||
model_names = [m.get("name", "unknown") for m in models]
|
||||
logger.info(f"✓ Ollama connection successful (found {len(models)} model(s): {', '.join(model_names[:3])}{'...' if len(model_names) > 3 else ''})")
|
||||
return True, ""
|
||||
else:
|
||||
error_message = f"Ollama API returned status {response.status_code}: {response.text}"
|
||||
logger.error(f"✗ Ollama connection failed: {error_message}")
|
||||
return False, error_message
|
||||
elif provider == "openai":
|
||||
logger.info(f"Checking OpenAI-compatible API connection to {base_url}...")
|
||||
if not api_key:
|
||||
logger.warning("OpenAI provider requires API key, skipping connection check")
|
||||
return True, ""
|
||||
else:
|
||||
error_message = f"Ollama API returned status {response.status_code}: {response.text}"
|
||||
logger.error(f"✗ Ollama connection failed: {error_message}")
|
||||
return False, error_message
|
||||
with httpx.Client(timeout=10.0, headers=headers) as client:
|
||||
response = client.get(f"{base_url}/models")
|
||||
if response.status_code == 200:
|
||||
models = response.json().get("data", [])
|
||||
model_names = [m.get("id", "unknown") for m in models]
|
||||
logger.info(f"✓ OpenAI-compatible API connection successful (found {len(models)} model(s): {', '.join(model_names[:3])}{'...' if len(model_names) > 3 else ''})")
|
||||
return True, ""
|
||||
else:
|
||||
error_message = f"OpenAI API returned status {response.status_code}: {response.text}"
|
||||
logger.error(f"✗ OpenAI connection failed: {error_message}")
|
||||
return False, error_message
|
||||
else:
|
||||
logger.warning(f"Unknown LLM provider: {provider}, skipping connection check")
|
||||
return True, ""
|
||||
|
||||
except httpx.ConnectError as e:
|
||||
error_message = f"Cannot connect to Ollama server at {settings.OLLAMA_BASE_URL}. " \
|
||||
f"Please check if Ollama server is running and accessible."
|
||||
logger.error(f"✗ Ollama connection failed: {error_message}")
|
||||
error_message = f"Cannot connect to {provider} server at {base_url}. " \
|
||||
f"Please check if server is running and accessible."
|
||||
logger.error(f"✗ {provider} connection failed: {error_message}")
|
||||
return False, error_message
|
||||
except httpx.TimeoutException:
|
||||
error_message = f"Connection to Ollama server at {settings.OLLAMA_BASE_URL} timed out. " \
|
||||
f"Please check if Ollama server is running and accessible."
|
||||
logger.error(f"✗ Ollama connection failed: {error_message}")
|
||||
error_message = f"Connection to {provider} server at {base_url} timed out. " \
|
||||
f"Please check if server is running and accessible."
|
||||
logger.error(f"✗ {provider} connection failed: {error_message}")
|
||||
return False, error_message
|
||||
except Exception as e:
|
||||
error_message = f"Unexpected error while checking Ollama connection: {str(e)}"
|
||||
logger.error(f"✗ Ollama connection check failed: {error_message}")
|
||||
error_message = f"Unexpected error while checking {provider} connection: {str(e)}"
|
||||
logger.error(f"✗ {provider} connection check failed: {error_message}")
|
||||
return False, error_message
|
||||
|
||||
def __init__(
|
||||
|
|
@ -129,34 +159,56 @@ class RAGEngine:
|
|||
request_timeout: float = 120.0,
|
||||
):
|
||||
self.vector_store_manager = vector_store_manager
|
||||
# Configurable LLM / prompt parameters
|
||||
self._user_prompt_template = prompt_template
|
||||
self._system_prompt = system_prompt
|
||||
self._temperature = temperature
|
||||
self._request_timeout = request_timeout
|
||||
|
||||
# Check Ollama connection before initializing
|
||||
is_connected, error_message = self.check_ollama_connection()
|
||||
llm_config = settings.get_llm_config()
|
||||
provider = llm_config["provider"]
|
||||
base_url = llm_config["base_url"]
|
||||
model = llm_config["model"]
|
||||
api_key = llm_config["api_key"]
|
||||
|
||||
is_connected, error_message = self.check_llm_connection(provider, base_url, model, api_key)
|
||||
if not is_connected:
|
||||
error_msg = (
|
||||
f"Error: Cannot connect to Ollama server.\n"
|
||||
f"Error: Cannot connect to {provider} server.\n"
|
||||
f" {error_message}\n"
|
||||
f"Connection info: {settings.OLLAMA_BASE_URL}\n"
|
||||
f"Connection info: {base_url}\n"
|
||||
f"Please check:\n"
|
||||
f" 1. Ollama server is running\n"
|
||||
f" 2. Ollama server is accessible from this host\n"
|
||||
f" 3. OLLAMA_BASE_URL is correctly configured\n"
|
||||
f" 4. Firewall rules allow connection to Ollama port"
|
||||
f" 1. {provider} server is running\n"
|
||||
f" 2. {provider} server is accessible from this host\n"
|
||||
f" 3. LLM_BASE_URL is correctly configured\n"
|
||||
f" 4. Firewall rules allow connection"
|
||||
)
|
||||
logger.error(error_msg)
|
||||
raise RuntimeError(error_msg)
|
||||
|
||||
self.llm = Ollama(
|
||||
model=settings.OLLAMA_MODEL,
|
||||
base_url=settings.OLLAMA_BASE_URL,
|
||||
temperature=self._temperature,
|
||||
request_timeout=self._request_timeout,
|
||||
)
|
||||
if provider == "ollama":
|
||||
self.llm = Ollama(
|
||||
model=model,
|
||||
base_url=base_url,
|
||||
temperature=self._temperature,
|
||||
request_timeout=self._request_timeout,
|
||||
)
|
||||
elif provider == "openai":
|
||||
self.llm = OpenAI(
|
||||
model=model,
|
||||
base_url=base_url,
|
||||
api_key=api_key,
|
||||
temperature=self._temperature,
|
||||
timeout=self._request_timeout,
|
||||
)
|
||||
else:
|
||||
self.llm = Ollama(
|
||||
model=model,
|
||||
base_url=base_url,
|
||||
temperature=self._temperature,
|
||||
request_timeout=self._request_timeout,
|
||||
)
|
||||
|
||||
self._llm_provider = provider
|
||||
|
||||
def extract_text_from_chunk(self, chunk) -> Optional[str]:
|
||||
if hasattr(chunk, 'delta'):
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ from chromadb.config import Settings as ChromaSettings
|
|||
from llama_index.vector_stores.chroma import ChromaVectorStore
|
||||
from llama_index.core import VectorStoreIndex, StorageContext
|
||||
from llama_index.embeddings.ollama import OllamaEmbedding
|
||||
from llama_index.embeddings.openai import OpenAIEmbedding
|
||||
from loguru import logger
|
||||
from config import settings
|
||||
|
||||
|
|
@ -106,11 +107,31 @@ class VectorStoreManager:
|
|||
metadata={"hnsw:space": "cosine"}
|
||||
)
|
||||
|
||||
# Initialize embedding model
|
||||
self.embed_model = OllamaEmbedding(
|
||||
model_name=settings.OLLAMA_EMBEDDING_MODEL,
|
||||
base_url=settings.OLLAMA_BASE_URL
|
||||
)
|
||||
# Initialize embedding model based on provider
|
||||
embed_config = settings.get_embedding_config()
|
||||
provider = embed_config["provider"]
|
||||
base_url = embed_config["base_url"]
|
||||
model = embed_config["model"]
|
||||
api_key = embed_config["api_key"]
|
||||
|
||||
if provider == "ollama":
|
||||
self.embed_model = OllamaEmbedding(
|
||||
model_name=model,
|
||||
base_url=base_url
|
||||
)
|
||||
elif provider == "openai":
|
||||
self.embed_model = OpenAIEmbedding(
|
||||
model=model,
|
||||
base_url=base_url,
|
||||
api_key=api_key
|
||||
)
|
||||
else:
|
||||
self.embed_model = OllamaEmbedding(
|
||||
model_name=model,
|
||||
base_url=base_url
|
||||
)
|
||||
|
||||
logger.info(f"Initialized {provider} embedding model: {model}")
|
||||
|
||||
# Create ChromaVectorStore
|
||||
self.vector_store = ChromaVectorStore(chroma_collection=self.collection)
|
||||
|
|
|
|||
Loading…
Reference in New Issue