dev-strender's picture
Replace v24-era demo with v34 pipeline demo (engine-vendored bundle)
9c84f9d verified
Raw History Blame Contribute Delete
3.74 kB
"""`llm_completion` μŠ€ν… -- input_fields 둜 μœ μ € λ©”μ‹œμ§€λ₯Ό λ§Œλ“€μ–΄ ν•œ 번 호좜.
`single_step.py`(SingleStepPipeline) μ—μ„œ 둜직 이동 (2026-08-10, λ™μž‘ 동일).
λ²ˆμ—­/μš”μ•½μ²˜λŸΌ "μž…λ ₯ ν•„λ“œλ“€ β†’ μ™„μ„±λ¬Έ ν•˜λ‚˜" ν˜•νƒœμ˜ νƒœμŠ€ν¬κ°€ μ“΄λ‹€.
`pipeline: single_step` 선언은 이 μŠ€ν… ν•˜λ‚˜μ§œλ¦¬ νŒŒμ΄ν”„λΌμΈμœΌλ‘œ ν•΄μ„λœλ‹€.
"""
import logging
from typing import Any
from solar_eval.models.sample import EvalSample
from solar_eval.pipelines.pipeline import BaseStep, StepContext
from solar_eval.pipelines.registry import register_step
logger = logging.getLogger(__name__)
@register_step("llm_completion")
class LLMCompletionStep(BaseStep):
"""Single LLM call step. Used for translation and summarization."""
async def run(self, sample: EvalSample, ctx: StepContext) -> None:
input_data = self._input_fields_dict(sample.input)
if ctx.messages:
# Structured messages mode: substitute {{field}} placeholders
resolved = self._resolve_messages(ctx.messages, input_data)
else:
# Legacy mode: system prompt + auto-built user message
user_content = self._build_user_message(input_data)
resolved = [
{"role": "system", "content": ctx.system_prompt},
{"role": "user", "content": user_content},
]
result = await ctx.provider.chat_completion(
messages=resolved,
model=self.config.get("model", ctx.model),
temperature=self.config.get("temperature", ctx.temperature),
max_tokens=self.config.get("max_tokens", ctx.max_tokens),
reasoning_effort=self.config.get("reasoning_effort", ctx.reasoning_effort),
)
ctx.text = result["content"]
ctx.add_usage(result.get("usage", {}))
sample.artifacts["think_content"] = result.get("think_content", "")
sample.artifacts["finish_reason"] = result.get("finish_reason", "stop")
def _input_fields_dict(self, sample_input: Any) -> dict[str, Any]:
"""`sample.input` 을 {ν•„λ“œλͺ…: κ°’} dict 둜 μ •κ·œν™”ν•œλ‹€.
λŸ¬λ„ˆλŠ” `task_config`의 `input_fields` μ „μ²΄λ‘œ λ§Œλ“  dict λ₯Ό κ·ΈλŒ€λ‘œ
`sample.input` 에 λ‹΄μ•„ λ„˜κΈ΄λ‹€(단일 ν•„λ“œλ“  닀쀑 ν•„λ“œλ“ ). μ—¬κΈ°μ„œλŠ”
κ·Έ κ΄€λ‘€λ₯Ό κ·ΈλŒ€λ‘œ λ°›λ˜, λ‹€λ₯Έ shape 이 λ“€μ–΄μ˜€λ©΄ 쑰용히 λ¬΄μ‹œν•˜μ§€ μ•Šκ³ 
λ°”λ‘œ μ‹€νŒ¨μ‹œν‚¨λ‹€.
"""
if sample_input is None:
return {}
if isinstance(sample_input, dict):
return sample_input
raise TypeError(
"LLMCompletionStep expects sample.input to be a dict of "
f"{{field: value}} (runner.py convention); got {type(sample_input)}"
)
def _resolve_messages(
self, messages: list[dict[str, str]], input_data: dict[str, Any]
) -> list[dict[str, str]]:
"""Substitute {{field_name}} placeholders in message content."""
resolved = []
for msg in messages:
content = msg["content"]
for field, value in input_data.items():
content = content.replace(f"{{{{{field}}}}}", str(value))
resolved.append({"role": msg["role"], "content": content})
return resolved
def _build_user_message(self, input_data: dict[str, Any]) -> str:
input_fields = self.env.input_fields
if len(input_fields) == 1:
return str(input_data.get(input_fields[0], ""))
# Multiple fields: format as labeled sections
parts = []
for field in input_fields:
value = input_data.get(field, "")
parts.append(f"{field}:\n{value}")
return "\n\n".join(parts)