Files
language-llm/backend/app/services/llm_service.py
2026-02-12 18:45:10 +01:00

66 lines
2.2 KiB
Python

"""FluentGerman.ai — LLM service (provider-agnostic via LiteLLM)."""
import logging
from collections.abc import AsyncGenerator
import litellm
from app.config import get_settings
logger = logging.getLogger("fluentgerman.llm")
def _resolve_model(model: str, provider: str) -> str:
"""Ensure Gemini models have the 'gemini/' prefix for Google AI Studio."""
if provider == "gemini" and not model.startswith("gemini/"):
resolved = f"gemini/{model}"
logger.info("Auto-prefixed model: %s%s (Google AI Studio)", model, resolved)
return resolved
return model
async def chat_stream(
messages: list[dict[str, str]],
model: str | None = None,
) -> AsyncGenerator[str, None]:
"""Stream chat completion tokens from the configured LLM provider."""
settings = get_settings()
model = _resolve_model(model or settings.llm_model, settings.llm_provider)
logger.info("LLM request: model=%s messages=%d", model, len(messages))
response = await litellm.acompletion(
model=model,
messages=messages,
api_key=settings.llm_api_key,
stream=True,
)
async for chunk in response:
delta = chunk.choices[0].delta
if delta.content:
yield delta.content
async def summarize_instruction(raw_text: str) -> str:
"""Ask the LLM to distill raw voice transcript into a structured instruction."""
settings = get_settings()
model = _resolve_model(settings.llm_model, settings.llm_provider)
meta_prompt = (
"You are an expert assistant for a language teacher. "
"The following text is a raw transcript of the teacher describing a learning instruction, "
"homework, or teaching method. Distill it into a clear, concise, structured instruction "
"that can be used as a system prompt for a language-learning LLM assistant. "
"Output ONLY the instruction text, no preamble.\n\n"
f"Transcript:\n{raw_text}"
)
response = await litellm.acompletion(
model=model,
messages=[{"role": "user", "content": meta_prompt}],
api_key=settings.llm_api_key,
)
return response.choices[0].message.content.strip()