nitdaa / agents /llm.py
AI Agent
Fix keyring module not found and add model name to metrics
0d4a982
Raw
History Blame Contribute Delete
4.15 kB
"""Local LLM wrapper β€” wraps the local completions endpoint as a LangChain LLM."""
from __future__ import annotations
from typing import Any, Optional
import requests, sys, os
sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
import config
from crewai.llms.base_llm import BaseLLM
from pydantic import Field
class LocalLLM(BaseLLM):
"""CrewAI-compatible wrapper for the local OpenAI-compatible completions endpoint."""
def __init__(self, **kwargs):
kwargs.setdefault("model", config.LLM_MODEL_ID)
kwargs.setdefault("base_url", config.LLM_BASE_URL)
super().__init__(**kwargs)
self.max_tokens = kwargs.get("max_tokens", config.LLM_MAX_TOKENS)
self.temperature = kwargs.get("temperature", config.LLM_TEMPERATURE)
self.top_p = kwargs.get("top_p", config.LLM_TOP_P)
self.timeout = kwargs.get("timeout", config.LLM_TIMEOUT)
self.use_kv_cache = kwargs.get("use_kv_cache", False)
def call(self, messages: list[dict], callbacks: list[Any] | None = None, **kwargs: Any) -> str:
payload = {
"model_id": self.model,
"prompt": messages,
"max_tokens": kwargs.get("max_tokens") or self.max_tokens,
"temperature": self.temperature,
"top_p": self.top_p,
"use_kv_cache": self.use_kv_cache,
"use_gpu": kwargs.get("use_gpu", False),
"cpu_threads": kwargs.get("cpu_threads", 2),
"llm_mode": kwargs.get("llm_mode", "expert"),
"attachments": [],
}
try:
resp = requests.post(
f"{self.base_url}/v1/completions",
json=payload,
timeout=self.timeout,
verify=False,
)
# ── Handle 503 "Server busy" explicitly ───────────────────────────
if resp.status_code == 503:
err_body = resp.json() if resp.content else {}
retry_hint = err_body.get("retry_after", 10)
reason = err_body.get("error", "The inference server is busy.")
self._last_prompt_tokens = 0
self._last_completion_tokens = 0
return (
f"[LLM BUSY] {reason} "
f"The server is processing another request. "
f"Please try again in {retry_hint} seconds."
)
resp.raise_for_status()
data = resp.json()
usage = data.get("usage", {})
# Store token counts so callers can read them without CrewAI usage_metrics
self._last_prompt_tokens = usage.get("prompt_tokens", 0)
self._last_completion_tokens = usage.get("completion_tokens", 0)
self._last_model_name = data.get("model", self.model)
if usage:
self._track_token_usage_internal(usage)
return data["choices"][0]["text"].strip()
except requests.exceptions.ConnectionError:
self._last_prompt_tokens = 0
self._last_completion_tokens = 0
return "[LLM OFFLINE] Cannot connect to the inference server. Is nvidia_llm.py running?"
except requests.exceptions.Timeout:
self._last_prompt_tokens = 0
self._last_completion_tokens = 0
return (
f"[LLM TIMEOUT] The inference server did not respond within {self.timeout}s. "
"The server may be busy. Please try again shortly."
)
except Exception as e:
self._last_prompt_tokens = 0
self._last_completion_tokens = 0
return f"[LLM ERROR] {e}"
def supports_function_calling(self) -> bool:
return False
def supports_stop_words(self) -> bool:
return False
# Singleton instance
_llm_instance: LocalLLM | None = None
def get_llm() -> LocalLLM:
global _llm_instance
if _llm_instance is None:
_llm_instance = LocalLLM(model=config.LLM_MODEL_ID)
return _llm_instance