Spaces:
Sleeping
Sleeping
File size: 8,505 Bytes
c44fab6 1961f74 c44fab6 1961f74 c44fab6 1961f74 c44fab6 1961f74 c44fab6 1961f74 c44fab6 1961f74 c44fab6 1961f74 c44fab6 1961f74 c44fab6 1961f74 c44fab6 1961f74 c44fab6 1961f74 c44fab6 1961f74 c44fab6 1961f74 c44fab6 1961f74 c44fab6 1961f74 c44fab6 1961f74 c44fab6 1961f74 c44fab6 1961f74 | 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 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | from __future__ import annotations
import logging
from app.clients.llm_router import chat_completion
from app.config import settings
from app.services.model_picker import pick_general_purpose_model
LOG = logging.getLogger(__name__)
_ROLE_WRITER_SYSTEM = (
"You are a helpful assistant that creates role prompts for LLM participants. "
"Respond ONLY with the finished role prompt text — no reasoning, analysis, "
"draft notes, questions to the user, or meta-commentary.\n\n"
"STRICT RULES:\n"
"- Do NOT invent a personal name unless one is explicitly provided in the name field.\n"
"- Do NOT invent hobbies, side interests, employers, blogs, websites, or domains "
"not stated in the user's input.\n"
"- Do NOT add conversational filler or questions (e.g. 'What do you think?').\n"
"- Write in second person ('You are…') so another LLM can embody the persona."
)
_ANTI_INVENTION_AI = (
"\n\nIMPORTANT: You may elaborate on tone, speech patterns, and professional style "
"implied by the stated identity, but stay strictly within the user's described domain. "
"Do NOT add unrelated interests, fictional backstory, or new subject areas."
)
_ANTI_INVENTION_EXACT = (
"\n\nIMPORTANT: Use ONLY the information explicitly provided. Do not invent names "
"(unless given in the name field), hobbies, employers, blogs, or other facts."
)
# ---------------------------------------------------------------------------
# Structured input prompts
# ---------------------------------------------------------------------------
STRUCTURED_AI_COMPLETED_PROMPT = (
"You will receive structured information about a character or persona: a name, an identity "
"statement, a profile, and optionally writing/speech samples. Some fields may be sparse or "
"missing. Write a complete, vivid 3-5 sentence role prompt that an LLM can use to "
"convincingly embody this persona in a conversation.\n\n"
"If any fields are sparse, infer plausible personality traits, speech patterns, and "
"conversational style from whatever clues are available — but only within the professional "
"or personal domain the user described. Fill in realistic detail so the role prompt is "
"rich and actionable — never produce a vague or skeletal prompt.\n\n"
"Cover: personality, tone and speech patterns, background/expertise, and how they would "
"naturally interact in a group discussion.\n\n"
"The name is: {name}\n"
"The identity statement is: {identity}\n"
"The profile is: {profile}\n"
"Here are the writing and/or speech samples: {samples}"
+ _ANTI_INVENTION_AI
)
STRUCTURED_EXACT_PROMPT = (
"You will receive structured information about a character or persona: a name, an identity "
"statement, a profile, and optionally writing/speech samples. Combine this information into "
"a coherent 3-5 sentence role prompt that an LLM can use to embody this persona in a "
"conversation.\n\n"
"IMPORTANT: Use ONLY the information explicitly provided. Do not invent, assume, or infer "
"any traits, background, opinions, or speech patterns beyond what is stated. Your job is "
"purely to organize and lightly rephrase the provided facts into a smooth, usable role "
"prompt — add linking words and natural sentence flow, but no new content. If a field is "
"empty or says '(not provided)', simply omit it.\n\n"
"The name is: {name}\n"
"The identity statement is: {identity}\n"
"The profile is: {profile}\n"
"Here are the writing and/or speech samples: {samples}"
+ _ANTI_INVENTION_EXACT
)
# ---------------------------------------------------------------------------
# Freeform input prompts
# ---------------------------------------------------------------------------
FREEFORM_AI_COMPLETED_PROMPT = (
"You will receive freeform information about a character or persona. The input may be "
"detailed (with writing samples, background, etc.) or very brief (just a name or a short "
"description). Regardless of how much is provided, write a complete, vivid 3-5 sentence "
"role prompt that an LLM can use to convincingly embody this persona in a conversation.\n\n"
"If the input is sparse, infer plausible personality traits, speech patterns, and "
"conversational style from whatever clues are available (the name, any title or "
"occupation, context, etc.) — but only within the domain the user described. Fill in "
"realistic detail so the role prompt is rich and actionable — never produce a vague or "
"skeletal prompt.\n\n"
"Cover: personality, tone and speech patterns, background/expertise, and how they would "
"naturally interact in a group discussion.\n\n"
"The persona's name is: {name}\n\n"
"Here is everything provided about this persona:\n"
"---\n{text}\n---"
+ _ANTI_INVENTION_AI
)
FREEFORM_EXACT_PROMPT = (
"You will receive freeform information about a character or persona. Combine this "
"information into a coherent 3-5 sentence role prompt that an LLM can use to embody "
"this persona in a conversation.\n\n"
"IMPORTANT: Use ONLY the information explicitly provided. Do not invent, assume, or infer "
"any traits, background, opinions, or speech patterns beyond what is stated. Your job is "
"purely to organize and lightly rephrase the user's text into a smooth, usable role "
"prompt — add linking words and natural sentence flow, but no new content. If very little "
"was provided, the role prompt should be correspondingly brief.\n\n"
"The persona's name is: {name}\n\n"
"Here is everything provided about this persona:\n"
"---\n{text}\n---"
+ _ANTI_INVENTION_EXACT
)
async def _call_llm(model_id: str, prompt_text: str) -> dict:
resolved = settings.resolve_model(model_id)
if not resolved:
return {
"role_prompt": "",
"error": f"No neutral model available to generate the role prompt.",
}
messages = [
{"role": "system", "content": _ROLE_WRITER_SYSTEM},
{"role": "user", "content": prompt_text},
]
result = await chat_completion(
resolved=resolved,
messages=messages,
temperature=0.7,
max_tokens=512,
timeout=45,
)
if result.get("error"):
return {"role_prompt": "", "error": result["response"]}
return {
"role_prompt": result["response"],
"elapsed_seconds": result["elapsed_seconds"],
"writer_model_id": model_id,
}
async def _resolve_writer_model(
orchestrator_model_id: str | None,
extra_model_ids: list[str] | None,
) -> str | None:
return pick_general_purpose_model(
orchestrator_model_id,
extra_model_ids=extra_model_ids,
)
async def generate_role_prompt(
name: str,
profile: str,
identity: str,
samples: str,
role_style: str = "exact",
orchestrator_model_id: str | None = None,
extra_model_ids: list[str] | None = None,
) -> dict:
"""Distill structured persona inputs into a role prompt via a neutral writer model."""
writer_id = await _resolve_writer_model(orchestrator_model_id, extra_model_ids)
if not writer_id:
return {"role_prompt": "", "error": "No model available to generate the role prompt."}
template = STRUCTURED_AI_COMPLETED_PROMPT if role_style == "ai_completed" else STRUCTURED_EXACT_PROMPT
prompt_text = template.format(
name=name or "(not provided)",
identity=identity or "(not provided)",
profile=profile or "(not provided)",
samples=samples or "(not provided)",
)
return await _call_llm(writer_id, prompt_text)
async def generate_role_prompt_freeform(
name: str,
text: str,
role_style: str = "ai_completed",
orchestrator_model_id: str | None = None,
extra_model_ids: list[str] | None = None,
) -> dict:
"""Distill a freeform text block into a role prompt via a neutral writer model."""
writer_id = await _resolve_writer_model(orchestrator_model_id, extra_model_ids)
if not writer_id:
return {"role_prompt": "", "error": "No model available to generate the role prompt."}
template = FREEFORM_AI_COMPLETED_PROMPT if role_style == "ai_completed" else FREEFORM_EXACT_PROMPT
prompt_text = template.format(
name=name or "(not provided)",
text=text or "(not provided)",
)
return await _call_llm(writer_id, prompt_text)
|