initial commit
This commit is contained in:
65
backend/app/services/llm_service.py
Normal file
65
backend/app/services/llm_service.py
Normal file
@@ -0,0 +1,65 @@
|
||||
"""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()
|
||||
Reference in New Issue
Block a user