Initial commit: RAGGER - Sistema RAG per associazione di volontariato
This commit is contained in:
commit
0204feb8bf
36 files changed
+4116
No files matched your search
@@ -0,0 +1,189 @@
|
||||
import httpx
|
||||
import warnings
|
||||
from typing import Optional
|
||||
from config import settings
|
||||
from logger import get_logger
|
||||
|
||||
warnings.filterwarnings("ignore", message="Unverified HTTPS request")
|
||||
|
||||
log = get_logger("ollama_client")
|
||||
|
||||
PROVIDER_BASE_URLS = {
|
||||
"openrouter": "https://openrouter.ai/api/v1",
|
||||
"openai": "https://api.openai.com/v1",
|
||||
"groq": "https://api.groq.com/openai/v1",
|
||||
"opencode": "https://opencode.ai/zen/v1",
|
||||
}
|
||||
|
||||
|
||||
class LLMClient:
|
||||
def __init__(self):
|
||||
self.provider = settings.LLM_PROVIDER
|
||||
self.timeout = httpx.Timeout(300.0, connect=30.0)
|
||||
log.info(f"Initializing LLMClient: provider={self.provider}")
|
||||
|
||||
if self.provider == "ollama":
|
||||
self.host = settings.OLLAMA_HOST
|
||||
log.debug(f"Ollama host: {self.host}")
|
||||
self.client = httpx.Client(timeout=self.timeout, verify=False)
|
||||
else:
|
||||
base_url = settings.LLM_BASE_URL or PROVIDER_BASE_URLS.get(self.provider, "")
|
||||
api_key = settings.LLM_API_KEY or ""
|
||||
log.debug(f"External API: {self.provider} -> {base_url}")
|
||||
self.client = httpx.Client(
|
||||
timeout=self.timeout,
|
||||
base_url=base_url,
|
||||
headers={
|
||||
"Authorization": f"Bearer {api_key}",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
)
|
||||
if self.provider == "openrouter":
|
||||
self.client.headers["HTTP-Referer"] = "https://github.com/ragger"
|
||||
self.client.headers["X-Title"] = "RAGGER"
|
||||
|
||||
def embed(self, text: str) -> list[float]:
|
||||
model = settings.OLLAMA_EMBED_MODEL
|
||||
log.debug(f"Embedding: model={model}, text_len={len(text)}")
|
||||
|
||||
url = f"{settings.OLLAMA_HOST.rstrip('/')}/api/embeddings"
|
||||
log.debug(f"POST {url}")
|
||||
|
||||
try:
|
||||
response = self.client.post(
|
||||
url,
|
||||
json={"model": model, "prompt": text}
|
||||
)
|
||||
response.raise_for_status()
|
||||
embedding = response.json()["embedding"]
|
||||
log.debug(f"Embedding success: {len(embedding)} dims")
|
||||
return embedding
|
||||
except httpx.HTTPStatusError as e:
|
||||
log.error(f"HTTP error: {e.response.status_code} - {e.response.text}")
|
||||
raise
|
||||
except Exception as e:
|
||||
log.error(f"Embedding error: {e}")
|
||||
raise
|
||||
|
||||
def generate(
|
||||
self,
|
||||
prompt: str,
|
||||
system: Optional[str] = None,
|
||||
context: Optional[str] = None
|
||||
) -> str:
|
||||
log.debug(f"Generate: provider={self.provider}, prompt_len={len(prompt)}")
|
||||
|
||||
if self.provider == "ollama":
|
||||
return self._generate_ollama(prompt, system, context)
|
||||
else:
|
||||
return self._generate_openai(prompt, system, context)
|
||||
|
||||
def _generate_ollama(
|
||||
self,
|
||||
prompt: str,
|
||||
system: Optional[str] = None,
|
||||
context: Optional[str] = None
|
||||
) -> str:
|
||||
model = settings.OLLAMA_MODEL
|
||||
log.debug(f"Ollama generate: model={model}")
|
||||
|
||||
full_prompt = ""
|
||||
if system:
|
||||
full_prompt += f"System: {system}\n\n"
|
||||
if context:
|
||||
full_prompt += f"Contesto:\n{context}\n\n"
|
||||
full_prompt += prompt
|
||||
|
||||
log.debug(f"Full prompt length: {len(full_prompt)} chars")
|
||||
|
||||
url = f"{settings.OLLAMA_HOST.rstrip('/')}/api/generate"
|
||||
log.debug(f"POST {url}")
|
||||
|
||||
try:
|
||||
response = self.client.post(
|
||||
url,
|
||||
json={"model": model, "prompt": full_prompt, "stream": False}
|
||||
)
|
||||
response.raise_for_status()
|
||||
result = response.json()["response"]
|
||||
log.debug(f"Response length: {len(result)} chars")
|
||||
log.info(f"Generated response ({len(result)} chars)")
|
||||
return result
|
||||
except httpx.HTTPStatusError as e:
|
||||
log.error(f"HTTP error: {e.response.status_code} - {e.response.text}")
|
||||
raise
|
||||
except Exception as e:
|
||||
log.error(f"Generation error: {e}")
|
||||
raise
|
||||
|
||||
def _generate_openai(
|
||||
self,
|
||||
prompt: str,
|
||||
system: Optional[str] = None,
|
||||
context: Optional[str] = None
|
||||
) -> str:
|
||||
model = settings.LLM_MODEL or "openrouter/auto"
|
||||
log.debug(f"OpenAI generate: model={model}")
|
||||
|
||||
messages = []
|
||||
if system:
|
||||
messages.append({"role": "system", "content": system})
|
||||
|
||||
user_content = ""
|
||||
if context:
|
||||
user_content += f"Contesto:\n{context}\n\n"
|
||||
user_content += prompt
|
||||
messages.append({"role": "user", "content": user_content})
|
||||
|
||||
log.debug(f"Messages: {len(messages)} items")
|
||||
|
||||
try:
|
||||
response = self.client.post(
|
||||
"/chat/completions",
|
||||
json={"model": model, "messages": messages}
|
||||
)
|
||||
response.raise_for_status()
|
||||
result = response.json()["choices"][0]["message"]["content"]
|
||||
log.info(f"Generated response ({len(result)} chars)")
|
||||
return result
|
||||
except httpx.HTTPStatusError as e:
|
||||
log.error(f"HTTP error: {e.response.status_code} - {e.response.text}")
|
||||
raise
|
||||
except Exception as e:
|
||||
log.error(f"Generation error: {e}")
|
||||
raise
|
||||
|
||||
def is_available(self) -> bool:
|
||||
log.debug("Checking availability...")
|
||||
try:
|
||||
if self.provider == "ollama":
|
||||
url = f"{settings.OLLAMA_HOST.rstrip('/')}/api/tags"
|
||||
log.debug(f"GET {url}")
|
||||
response = self.client.get(url, timeout=5.0)
|
||||
available = response.status_code == 200
|
||||
log.debug(f"Ollama available: {available}")
|
||||
return available
|
||||
else:
|
||||
log.debug("External API - assuming available")
|
||||
return True
|
||||
except Exception as e:
|
||||
log.warning(f"Availability check failed: {e}")
|
||||
return False
|
||||
|
||||
def list_models(self) -> list[str]:
|
||||
if self.provider != "ollama":
|
||||
return []
|
||||
|
||||
try:
|
||||
url = f"{settings.OLLAMA_HOST.rstrip('/')}/api/tags"
|
||||
response = self.client.get(url, timeout=5.0)
|
||||
response.raise_for_status()
|
||||
models = response.json().get("models", [])
|
||||
return [m["name"] for m in models]
|
||||
except Exception as e:
|
||||
log.error(f"Error listing models: {e}")
|
||||
return []
|
||||
|
||||
|
||||
llm = LLMClient()
|
||||
ollama = llm
|
||||
Reference in new issue
Block a user