File size: 2,818 Bytes
2ff64f6
 
c38ccca
2ff64f6
 
 
 
c38ccca
923dda1
c38ccca
2ff64f6
923dda1
2ff64f6
c38ccca
 
2ff64f6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d39d907
c38ccca
2ff64f6
 
c38ccca
 
2ff64f6
 
 
 
 
c38ccca
d39d907
2ff64f6
 
 
b401a63
923dda1
2ff64f6
b401a63
 
 
 
 
 
2ff64f6
 
 
b401a63
 
2ff64f6
923dda1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2ff64f6
 
b401a63
 
 
2ff64f6
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
"""
Persona registry — now driven by ``config.yaml``.

The heavy persona definitions have moved into ``config.yaml`` (under the
``personas`` key).  This module reads them via :func:`app.config.get_settings`
and exposes the same public API the rest of the codebase already relies on.
"""

from typing import Dict, List, Optional

from app.config import get_settings
from app.llm.llm_client import LLMClient
from app.models.persona import Persona


def _build_personas_dict() -> dict:
    """Build the ``{id: {name, system_prompt, temperature}}`` registry from
    the YAML configuration."""
    cfg = get_settings()
    base_prompt = cfg.personas.base_prompt.strip()
    registry: dict = {}
    for p in cfg.personas.items:
        # Combine the persona-specific prompt with the shared base prompt
        full_prompt = p.persona_prompt.strip()
        if base_prompt:
            full_prompt = f"{full_prompt}\n\n{base_prompt}"
        registry[p.id] = {
            "name": p.name,
            "system_prompt": full_prompt,
            "default_temperature": p.temperature,
        }
    return registry


# Lazy singleton — built once on first access
_DEFAULT_PERSONAS: Optional[dict] = None


def _get_registry() -> dict:
    global _DEFAULT_PERSONAS
    if _DEFAULT_PERSONAS is None:
        _DEFAULT_PERSONAS = _build_personas_dict()
    return _DEFAULT_PERSONAS


# ------------------------------------------------------------------
# Public API — unchanged signatures so existing callers keep working
# ------------------------------------------------------------------

def get_default_personas(llm: LLMClient) -> List[Persona]:
    """Return a list of :class:`Persona` objects wired to *llm*."""
    return [
        Persona(
            id=pid,
            name=data["name"],
            system_prompt=data["system_prompt"],
            llm=llm,
            temperature=data.get("default_temperature", 5),
        )
        for pid, data in _get_registry().items()
    ]


def get_personas_with_llm_map(
    default_llm: LLMClient,
    llm_map: Optional[Dict[str, LLMClient]] = None,
) -> List[Persona]:
    if not llm_map:
        return get_default_personas(default_llm)
    return [
        Persona(
            id=pid,
            name=data["name"],
            system_prompt=data["system_prompt"],
            llm=llm_map.get(pid, default_llm),
            temperature=data.get("default_temperature", 5),
        )
        for pid, data in _get_registry().items()
    ]


def get_default_persona_prompt(persona_id: str) -> Optional[str]:
    data = _get_registry().get(persona_id)
    return data["system_prompt"] if data else None


def is_valid_persona_id(pid: str) -> bool:
    return pid in _get_registry()


def list_available_personas() -> List[str]:
    return list(_get_registry().keys())