File size: 6,714 Bytes
eda4739 | 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 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 | """Remote Tinker inference helpers for the Cree1865 Hugging Face Space."""
from __future__ import annotations
from dataclasses import asdict, dataclass
from functools import lru_cache
import time
from typing import Any, Callable
DEFAULT_MODEL_PATH = (
"tinker://c71aadd1-8e48-51b0-b890-149a2889b4fa:train:0/"
"sampler_weights/final"
)
DEFAULT_SYSTEM_PROMPT = (
"Answer Cree dictionary lookup and translation prompts concisely. "
"Use Cree forms only when you are confident, preserve orthography exactly, "
"and return only the requested answer."
)
EXAMPLE_PROMPTS = [
"Translate the Cree word maskihkiy into English.",
"Give the Cree dictionary headword for 'medicine'. Return only the Cree form.",
"Translate 'I speak Cree' into Cree. Return only the answer.",
"What does the Cree suffix -win usually mark in dictionary entries?",
]
@dataclass(frozen=True)
class TinkerGeneration:
"""Structured result from one Tinker sampler request."""
responses: list[str]
stop_reasons: list[str]
prompt_tokens: int
model_path: str
elapsed_seconds: float
def to_metadata(self) -> dict[str, Any]:
return asdict(self)
def _import_tinker() -> Any:
import tinker
return tinker
@lru_cache(maxsize=4)
def get_cached_sampling_client(model_path: str) -> Any:
"""Create and cache one remote sampler client per Tinker model path."""
tinker = _import_tinker()
service_client = tinker.ServiceClient()
return service_client.create_sampling_client(model_path=model_path)
def build_chat_prompt(
tokenizer: Any,
system_prompt: str,
user_prompt: str,
enable_thinking: bool,
) -> str:
"""Format a chat prompt with the sampler tokenizer template."""
messages = [
{"role": "system", "content": system_prompt.strip()},
{"role": "user", "content": user_prompt.strip()},
]
if hasattr(tokenizer, "apply_chat_template"):
try:
return tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True,
enable_thinking=enable_thinking,
)
except TypeError:
return tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True,
)
return f"{messages[0]['content']}\n\nUser: {messages[1]['content']}\nAssistant:"
def sample_tinker_response(
*,
prompt: str,
system_prompt: str,
model_path: str = DEFAULT_MODEL_PATH,
max_tokens: int = 96,
temperature: float = 0.3,
top_p: float = 0.9,
top_k: int = -1,
seed: int = 42,
num_samples: int = 1,
enable_thinking: bool = False,
sampling_client: Any | None = None,
tinker_module: Any | None = None,
) -> TinkerGeneration:
"""Run one remote generation request against a Tinker sampler checkpoint."""
if tinker_module is None:
tinker_module = _import_tinker()
if sampling_client is None:
sampling_client = get_cached_sampling_client(model_path)
start = time.perf_counter()
tokenizer = sampling_client.get_tokenizer()
formatted_prompt = build_chat_prompt(
tokenizer,
system_prompt=system_prompt,
user_prompt=prompt,
enable_thinking=enable_thinking,
)
prompt_tokens = tokenizer.encode(formatted_prompt)
model_input = tinker_module.ModelInput.from_ints(prompt_tokens)
sampling_params = tinker_module.SamplingParams(
max_tokens=int(max_tokens),
temperature=float(temperature),
top_p=float(top_p),
top_k=int(top_k),
seed=int(seed),
)
response = sampling_client.sample(
prompt=model_input,
num_samples=int(num_samples),
sampling_params=sampling_params,
).result()
decoded = [
tokenizer.decode(sequence.tokens, skip_special_tokens=True).strip()
for sequence in response.sequences
]
stop_reasons = [str(sequence.stop_reason) for sequence in response.sequences]
return TinkerGeneration(
responses=decoded,
stop_reasons=stop_reasons,
prompt_tokens=len(prompt_tokens),
model_path=model_path,
elapsed_seconds=round(time.perf_counter() - start, 3),
)
def format_output(responses: list[str]) -> str:
"""Render one or more model samples for the Gradio textbox."""
cleaned = [response.strip() or "[empty response]" for response in responses]
if len(cleaned) == 1:
return cleaned[0]
return "\n\n---\n\n".join(
f"Sample {index}\n{response}" for index, response in enumerate(cleaned, start=1)
)
def generate_for_ui(
*,
prompt: str,
system_prompt: str,
max_tokens: int,
temperature: float,
top_p: float,
seed: int,
num_samples: int,
enable_thinking: bool,
model_path: str = DEFAULT_MODEL_PATH,
sampling_client_factory: Callable[[str], Any] | None = None,
) -> tuple[str, dict[str, Any]]:
"""Gradio callback wrapper with validation and readable error reporting."""
if not prompt or not prompt.strip():
return (
"Enter a prompt before running inference.",
{"ok": False, "error_type": "validation"},
)
try:
sampling_client = (
sampling_client_factory(model_path)
if sampling_client_factory is not None
else get_cached_sampling_client(model_path)
)
result = sample_tinker_response(
prompt=prompt,
system_prompt=system_prompt or DEFAULT_SYSTEM_PROMPT,
model_path=model_path,
max_tokens=max_tokens,
temperature=temperature,
top_p=top_p,
top_k=-1,
seed=seed,
num_samples=num_samples,
enable_thinking=enable_thinking,
sampling_client=sampling_client,
)
metadata = result.to_metadata()
metadata.update(
{
"ok": True,
"num_samples": int(num_samples),
"temperature": float(temperature),
"top_p": float(top_p),
"seed": int(seed),
"enable_thinking": bool(enable_thinking),
}
)
return format_output(result.responses), metadata
except Exception as exc: # noqa: BLE001 - UI should surface backend failures.
return (
f"Inference error ({type(exc).__name__}): {exc}",
{
"ok": False,
"error_type": type(exc).__name__,
"message": str(exc),
"model_path": model_path,
},
)
|