Initial implementation of IRC LLM bot
Full implementation from spec: ZNC/IRC client with TLS, Ollama LLM backend, per-user SQLite conversation memory, and Flask web admin portal with 7 pages. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
70
bot/llm_client.py
Normal file
70
bot/llm_client.py
Normal file
@@ -0,0 +1,70 @@
|
||||
import httpx
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def generate(prompt: str, system: str, config: dict) -> str:
|
||||
host = config.get("ollama_host", "192.168.2.10")
|
||||
port = config.get("ollama_port", 11434)
|
||||
model = config.get("ollama_model", "llama3.1")
|
||||
timeout = config.get("response_timeout_seconds", 30)
|
||||
num_predict = config.get("ollama_num_predict", 120)
|
||||
num_ctx = config.get("ollama_num_ctx", 2048)
|
||||
temperature = config.get("ollama_temperature", 0.7)
|
||||
max_length = config.get("max_response_length", 400)
|
||||
|
||||
url = f"http://{host}:{port}/api/generate"
|
||||
payload = {
|
||||
"model": model,
|
||||
"system": system,
|
||||
"prompt": prompt,
|
||||
"stream": False,
|
||||
"options": {
|
||||
"temperature": temperature,
|
||||
"num_predict": num_predict,
|
||||
"num_ctx": num_ctx,
|
||||
},
|
||||
}
|
||||
|
||||
logger.debug(f"[LLM] POST {url} model={model} prompt_len={len(prompt)}")
|
||||
|
||||
try:
|
||||
response = httpx.post(url, json=payload, timeout=timeout)
|
||||
response.raise_for_status()
|
||||
text = response.json().get("response", "").strip()
|
||||
if len(text) > max_length:
|
||||
text = text[:max_length].rsplit(" ", 1)[0] + "…"
|
||||
logger.debug(f"[LLM] Response ({len(text)} chars): {text[:80]}")
|
||||
return text
|
||||
except httpx.TimeoutException:
|
||||
logger.error(f"[LLM] Timeout after {timeout}s")
|
||||
raise TimeoutError(f"Ollama did not respond within {timeout}s")
|
||||
except Exception as e:
|
||||
logger.error(f"[LLM] Request failed: {e}")
|
||||
raise
|
||||
|
||||
|
||||
def build_prompt(
|
||||
user_message: str,
|
||||
nick: str,
|
||||
persistent_history: list[dict],
|
||||
context_buffer: list[str],
|
||||
) -> str:
|
||||
parts = []
|
||||
|
||||
if persistent_history:
|
||||
parts.append("--- Past conversation with this user ---")
|
||||
for ex in persistent_history:
|
||||
parts.append(f"User: {ex['user']}")
|
||||
parts.append(f"Assistant: {ex['assistant']}")
|
||||
parts.append("--- End of past conversation ---")
|
||||
|
||||
if context_buffer:
|
||||
parts.append("--- Recent channel activity ---")
|
||||
parts.extend(context_buffer)
|
||||
parts.append("--- End of channel activity ---")
|
||||
|
||||
parts.append(f"{nick} asks: {user_message}")
|
||||
|
||||
return "\n".join(parts)
|
||||
Reference in New Issue
Block a user