Lucas
Add llama.cpp parser backend for Space
95cf15d
Raw
History Blame Contribute Delete
3.56 kB
import json
import logging
from time import perf_counter
from urllib import request
from training_coach.parser import (
PARSER_SYSTEM_PROMPT,
expected_response_format,
log_parser_messages,
log_parser_response_text,
parse_model_response,
)
from training_coach.models import ParsedCheckIn
OLLAMA_URL = "http://127.0.0.1:11434/api/generate"
OLLAMA_MODEL = "qwen3:1.7B"
OLLAMA_TINY_CANDIDATE = "qwen3:4b"
logger = logging.getLogger(__name__)
def build_ollama_prompt(raw_text: str) -> str:
schema = json.dumps(expected_response_format(), indent=2)
return (
"Parse this check-in into the expected response JSON schema.\n"
"Return only the JSON object.\n\n"
"Allowed top-level keys are: check_in, missing_fields, follow_up_items, "
"follow_up_questions, context_signals, notes.\n\n"
"The check_in object must use only these keys: raw_text, "
"time_available_minutes, energy_level, sleep_quality, sleep_hours, "
"soreness, pain_or_injury, pain_issues, mood_stress, notes.\n\n"
f"Expected response JSON schema:\n{schema}\n\n"
f"Check-in:\n{raw_text}"
)
def generate_parser_response_ollama(
raw_text: str,
model_name: str = OLLAMA_MODEL,
url: str = OLLAMA_URL,
) -> str:
start_time = perf_counter()
prompt = build_ollama_prompt(raw_text)
logger.info(
"event=parser_ollama_request model=%s prompt_chars=%s text_chars=%s",
model_name,
len(prompt),
len(raw_text),
)
log_parser_messages(
backend="ollama",
model_name=model_name,
messages=[
{"role": "system", "content": PARSER_SYSTEM_PROMPT},
{"role": "user", "content": prompt},
],
)
payload = {
"model": model_name,
"system": PARSER_SYSTEM_PROMPT,
"prompt": prompt,
"format": expected_response_format(),
"stream": False,
"think": False,
"options": {
"temperature": 0,
"num_predict": 512,
},
}
body = json.dumps(payload).encode("utf-8")
http_request = request.Request(
url,
data=body,
headers={"Content-Type": "application/json"},
method="POST",
)
with request.urlopen(http_request, timeout=120) as response:
data = json.loads(response.read().decode("utf-8"))
response_text = data["response"].strip()
logger.info(
"event=parser_ollama_response model=%s response_chars=%s "
"prompt_eval_count=%s eval_count=%s total_duration_ms=%s "
"load_duration_ms=%s prompt_eval_duration_ms=%s eval_duration_ms=%s "
"wall_elapsed_ms=%s",
model_name,
len(response_text),
data.get("prompt_eval_count"),
data.get("eval_count"),
_ns_to_ms(data.get("total_duration")),
_ns_to_ms(data.get("load_duration")),
_ns_to_ms(data.get("prompt_eval_duration")),
_ns_to_ms(data.get("eval_duration")),
round((perf_counter() - start_time) * 1000),
)
log_parser_response_text(
backend="ollama",
model_name=model_name,
response_text=response_text,
)
return response_text
def _ns_to_ms(value):
if value is None:
return None
return round(value / 1_000_000)
def parse_check_in_with_ollama(
raw_text: str,
model_name: str = OLLAMA_MODEL,
) -> ParsedCheckIn:
response_text = generate_parser_response_ollama(raw_text, model_name=model_name)
return parse_model_response(response_text)