Ctrl K
ComunidadGuías…

butler/transport/auxiliary_client.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
"""Cheap-model router for side tasks (compression, post-session extraction)."""

from __future__ import annotations

import asyncio
import logging
from collections.abc import Awaitable, Callable
from typing import Any

from butler.configuration.settings import ModelConfig, get_butler_settings
from butler.transport.llm_client import LLMClient

logger = logging.getLogger(__name__)


def resolve_auxiliary_config(task: str = "compression") -> ModelConfig:
    """Pick auxiliary model: ``config.yaml`` auxiliary.* → **default_provider** (MiniMax) → butler model."""
    settings = get_butler_settings()
    cfg = settings.get_auxiliary_task_config(task)
    if not cfg.is_empty():
        return cfg

    preferred = (settings.default_provider or "minimax").strip()
    if preferred and (pc := settings.providers.get(preferred)):
        return ModelConfig(provider=preferred, model=pc.model or "")

    from butler.model_resolve import resolve_effective_model

    butler_cfg = resolve_effective_model("butler", settings=settings).config
    if not butler_cfg.is_empty():
        return butler_cfg

    from butler.defaults.model_defaults import AUXILIARY_SCAN_PROVIDERS

    for name in AUXILIARY_SCAN_PROVIDERS:
        if (pc := settings.providers.get(name)):
            return ModelConfig(provider=name, model=pc.model or "")
    return ModelConfig(provider=preferred or "minimax", model="")


def create_auxiliary_client(task: str = "compression", **kwargs: Any) -> LLMClient:
    cfg = resolve_auxiliary_config(task)
    settings = get_butler_settings()
    pc = settings.providers.get(cfg.provider) if cfg.provider else None
    return LLMClient(
        provider=cfg.provider,
        model=cfg.model or (pc.model if pc else ""),
        api_key=pc.api_key if pc else None,
        base_url=pc.base_url if pc else None,
        max_tokens=cfg.max_tokens or 4096,
        temperature=cfg.temperature if cfg.temperature is not None else 0.3,
        **kwargs,
    )


def auxiliary_complete(
    prompt: str,
    *,
    task: str = "compression",
    system: str = "You are a concise assistant.",
    session_key: str = "",
) -> str:
    """Single-shot completion for side tasks."""
    client = create_auxiliary_client(task)
    resp = client.complete(
        messages=[
            {"role": "system", "content": system},
            {"role": "user", "content": prompt},
        ],
    )
    sk = session_key or _current_session_key()
    if sk and resp.usage:
        from butler.transport.auxiliary_client_ops import record_auxiliary_llm_cost_safe

        record_auxiliary_llm_cost_safe(sk, resp.usage)
    return resp.content or ""


def _current_session_key() -> str:
    from butler.transport.auxiliary_client_ops import current_auxiliary_session_key_safe

    return str(current_auxiliary_session_key_safe())


def auxiliary_llm_call_factory(
    task: str = "post_session",
) -> Callable[[str], Awaitable[str]]:
    """Async callable for PostSessionProcessor (blocking HTTP runs in a thread)."""

    async def _call(prompt: str) -> str:
        return await asyncio.to_thread(auxiliary_complete, prompt, task=task)

    return _call