Skip to content

Generation API Reference

vietquill.generation.controllable_paraphraser.AutoModelForParaphraseGeneration(hub_id: str = None, device: str = None, **kwargs)

Bases: BaseParaphraseGenerator

Paraphrase Generator using a single user-specified model without sentence/question splitting.

Initializes the Paraphrase Generator using a single model.

Parameters:

Name Type Description Default
hub_id str

Path or HF repo ID for the model repository or checkpoint.

None
device str

Device to run the model on ('cuda' or 'cpu').

None
**kwargs

Additional keyword arguments passed to AutoModelForSeq2SeqLM.from_pretrained.

{}
Source code in src/vietquill/generation/controllable_paraphraser/auto.py
def __init__(
    self,
    hub_id: str = None,
    device: str = None,
    **kwargs,
):
    """
    Initializes the Paraphrase Generator using a single model.

    Args:
        hub_id: Path or HF repo ID for the model repository or checkpoint.
        device: Device to run the model on ('cuda' or 'cpu').
        **kwargs: Additional keyword arguments passed to AutoModelForSeq2SeqLM.from_pretrained.
    """
    default_hub_id = get_config(
        "models.paraphraser.hub_id", MODELS["paraphraser"]["hub_id"]
    )
    self.hub_id = hub_id or default_hub_id
    self.device = (
        device if device else ("cuda" if torch.cuda.is_available() else "cpu")
    )

    logger.info(f"Loading tokenizer from {self.hub_id}...")
    self.tokenizer = AutoTokenizer.from_pretrained(self.hub_id)

    logger.info(f"Loading model from {self.hub_id}...")
    self.model = AutoModelForSeq2SeqLM.from_pretrained(self.hub_id, **kwargs)
    self.model.to(self.device)
    self.model.eval()

paraphrase(text: str, **kwargs) -> List[str]

Generates paraphrases with quality control constraints using the single loaded model.

Parameters:

Name Type Description Default
text str

Input text to paraphrase.

required
**kwargs

Generation parameters and control values. - semantic (int): Semantic control (0-100), default 90. - syntactic (int): Syntactic control (0-100), default 85. - lexical (int): Lexical control (0-100), default 80. - style (ParaphraseStyle | str): Style preset. Also sets decoding defaults (CONSERVATIVE: beam search, BALANCED/DIVERSE: nucleus sampling). - control_prefix (bool): Whether to prepend control prefix SEM_ SYN_ LEX_. Default True. - num_candidates (int): Number of sequences to return. - num_beams (int): Number of beams (1 with do_sample=True = plain sampling). - oversample (int): Extra sequences generated beyond num_candidates, default 2. - estimator: Quality estimator used with min_semantic; never loaded implicitly. - min_semantic (float): Drop candidates whose estimated semantic score is below this value (requires an estimator). - max_length (int): Max length. - and any other transformers.GenerationConfig parameters.

{}

Returns:

Type Description
List[str]

List of paraphrased strings.

Source code in src/vietquill/generation/controllable_paraphraser/auto.py
def paraphrase(self, text: str, **kwargs) -> List[str]:
    """
    Generates paraphrases with quality control constraints using the single loaded model.

    Args:
        text: Input text to paraphrase.
        **kwargs: Generation parameters and control values.
            - semantic (int): Semantic control (0-100), default 90.
            - syntactic (int): Syntactic control (0-100), default 85.
            - lexical (int): Lexical control (0-100), default 80.
            - style (ParaphraseStyle | str): Style preset. Also sets decoding defaults
              (CONSERVATIVE: beam search, BALANCED/DIVERSE: nucleus sampling).
            - control_prefix (bool): Whether to prepend control prefix SEM_ SYN_ LEX_. Default True.
            - num_candidates (int): Number of sequences to return.
            - num_beams (int): Number of beams (1 with do_sample=True = plain sampling).
            - oversample (int): Extra sequences generated beyond num_candidates, default 2.
            - estimator: Quality estimator used with min_semantic; never loaded implicitly.
            - min_semantic (float): Drop candidates whose estimated semantic score is
              below this value (requires an estimator).
            - max_length (int): Max length.
            - and any other transformers.GenerationConfig parameters.

    Returns:
        List of paraphrased strings.
    """
    control_prefix = kwargs.pop("control_prefix", True)
    sem_norm, syn_norm, lex_norm = self._resolve_controls(kwargs)
    estimator, min_semantic = self._pop_quality_filter(kwargs)
    input_text = self._format_input_text(
        text, sem_norm, syn_norm, lex_norm, control_prefix=control_prefix
    )
    gen_kwargs, max_length, num_candidates = self._prepare_gen_kwargs(kwargs)

    encoding = self.tokenizer(
        input_text,
        return_tensors="pt",
        padding=True,
        truncation=True,
        max_length=max_length,
    )
    input_ids = encoding["input_ids"].to(self.device)
    attention_mask = encoding["attention_mask"].to(self.device)

    with torch.no_grad():
        outputs = self.model.generate(
            input_ids=input_ids,
            attention_mask=attention_mask,
            **gen_kwargs,
        )

    candidates = self.tokenizer.batch_decode(outputs, skip_special_tokens=True)
    return self._postprocess(
        text, candidates, num_candidates, estimator, min_semantic
    )

paraphrases(texts: List[str], **kwargs) -> List[List[str]]

Generates paraphrases for a batch of texts using the single loaded model.

Parameters:

Name Type Description Default
texts List[str]

List of input texts to paraphrase.

required
**kwargs

Generation parameters and control values (same as paraphrase method). - batch_size (int, optional): Chunk size for batch processing.

{}

Returns:

Type Description
List[List[str]]

List of lists of paraphrased strings.

Source code in src/vietquill/generation/controllable_paraphraser/auto.py
def paraphrases(self, texts: List[str], **kwargs) -> List[List[str]]:
    """
    Generates paraphrases for a batch of texts using the single loaded model.

    Args:
        texts: List of input texts to paraphrase.
        **kwargs: Generation parameters and control values (same as paraphrase method).
            - batch_size (int, optional): Chunk size for batch processing.

    Returns:
        List of lists of paraphrased strings.
    """
    if not texts:
        return []

    control_prefix = kwargs.pop("control_prefix", True)
    sem_norm, syn_norm, lex_norm = self._resolve_controls(kwargs)
    estimator, min_semantic = self._pop_quality_filter(kwargs)
    batch_size = kwargs.pop("batch_size", None)
    gen_kwargs, max_length, num_candidates = self._prepare_gen_kwargs(kwargs)
    actual_num_return = gen_kwargs["num_return_sequences"]

    formatted_inputs = [
        self._format_input_text(
            t, sem_norm, syn_norm, lex_norm, control_prefix=control_prefix
        )
        for t in texts
    ]

    results = []
    step = batch_size or len(formatted_inputs)

    for start_idx in range(0, len(formatted_inputs), step):
        chunk_inputs = formatted_inputs[start_idx : start_idx + step]
        chunk_orig = texts[start_idx : start_idx + step]

        encoding = self.tokenizer(
            chunk_inputs,
            return_tensors="pt",
            padding=True,
            truncation=True,
            max_length=max_length,
        )
        input_ids = encoding["input_ids"].to(self.device)
        attention_mask = encoding["attention_mask"].to(self.device)

        with torch.no_grad():
            outputs = self.model.generate(
                input_ids=input_ids,
                attention_mask=attention_mask,
                **gen_kwargs,
            )

        decoded = self.tokenizer.batch_decode(outputs, skip_special_tokens=True)

        for i, orig_text in enumerate(chunk_orig):
            c_start = i * actual_num_return
            c_end = c_start + actual_num_return
            candidates = decoded[c_start:c_end]
            results.append(
                self._postprocess(
                    orig_text, candidates, num_candidates, estimator, min_semantic
                )
            )

    return results

vietquill.generation.controllable_paraphraser.EnsembleModelForParaphraseGeneration(hub_id: str = None, device: str = None)

Bases: BaseParaphraseGenerator

Quality Control Paraphraser with automatic routing between specialized sentence and question models.

Initializes the Ensemble Paraphraser with support for both sentence and question models.

Parameters:

Name Type Description Default
hub_id str

Path or HF name for the unified model repo.

None
device str

Device to run the model on.

None
Source code in src/vietquill/generation/controllable_paraphraser/ensemble.py
def __init__(self, hub_id: str = None, device: str = None):
    """
    Initializes the Ensemble Paraphraser with support for both sentence and question models.

    Args:
        hub_id: Path or HF name for the unified model repo.
        device: Device to run the model on.
    """
    self.hub_id = hub_id or get_config(
        "models.paraphraser.hub_id", MODELS["paraphraser"]["hub_id"]
    )
    self.device = (
        device if device else ("cuda" if torch.cuda.is_available() else "cpu")
    )

    logger.info(f"Loading tokenizer from {self.hub_id}...")
    self.tokenizer = AutoTokenizer.from_pretrained(self.hub_id)

    logger.info(f"Loading model from {self.hub_id}/sentence...")
    self.model_sentence = AutoModelForSeq2SeqLM.from_pretrained(
        self.hub_id, subfolder="sentence"
    )
    self.model_sentence.to(self.device)
    self.model_sentence.eval()

    logger.info(f"Loading model from {self.hub_id}/question...")
    self.model_question = AutoModelForSeq2SeqLM.from_pretrained(
        self.hub_id, subfolder="question"
    )
    self.model_question.to(self.device)
    self.model_question.eval()

paraphrase(text: str, **kwargs) -> List[str]

Generates paraphrases with quality control constraints. Automatically detects if the input is a question and routes to the corresponding model.

Parameters:

Name Type Description Default
text str

Input text to paraphrase.

required
**kwargs

Generation parameters and control values. - semantic (int): Semantic control (0-100), default 90. - syntactic (int): Syntactic control (0-100), default 85. - lexical (int): Lexical control (0-100), default 80. - style (ParaphraseStyle | str): Style preset. Also sets decoding defaults (CONSERVATIVE: beam search, BALANCED/DIVERSE: nucleus sampling). - num_candidates (int): Number of sequences to return. - num_beams (int): Number of beams (1 with do_sample=True = plain sampling). - oversample (int): Extra sequences generated beyond num_candidates, default 2. - estimator: Quality estimator used with min_semantic; never loaded implicitly. - min_semantic (float): Drop candidates whose estimated semantic score is below this value (requires an estimator). - max_length (int): Max length. - and any other transformers.GenerationConfig parameters.

{}

Returns:

Type Description
List[str]

List of paraphrased strings.

Source code in src/vietquill/generation/controllable_paraphraser/ensemble.py
def paraphrase(self, text: str, **kwargs) -> List[str]:
    """
    Generates paraphrases with quality control constraints.
    Automatically detects if the input is a question and routes to the corresponding model.

    Args:
        text: Input text to paraphrase.
        **kwargs: Generation parameters and control values.
            - semantic (int): Semantic control (0-100), default 90.
            - syntactic (int): Syntactic control (0-100), default 85.
            - lexical (int): Lexical control (0-100), default 80.
            - style (ParaphraseStyle | str): Style preset. Also sets decoding defaults
              (CONSERVATIVE: beam search, BALANCED/DIVERSE: nucleus sampling).
            - num_candidates (int): Number of sequences to return.
            - num_beams (int): Number of beams (1 with do_sample=True = plain sampling).
            - oversample (int): Extra sequences generated beyond num_candidates, default 2.
            - estimator: Quality estimator used with min_semantic; never loaded implicitly.
            - min_semantic (float): Drop candidates whose estimated semantic score is
              below this value (requires an estimator).
            - max_length (int): Max length.
            - and any other transformers.GenerationConfig parameters.

    Returns:
        List of paraphrased strings.
    """
    is_question = text.strip().endswith("?")
    model = self.model_question if is_question else self.model_sentence

    sem_norm, syn_norm, lex_norm = self._resolve_controls(kwargs)
    estimator, min_semantic = self._pop_quality_filter(kwargs)
    input_text = self._format_input_text(text, sem_norm, syn_norm, lex_norm)
    gen_kwargs, max_length, num_candidates = self._prepare_gen_kwargs(kwargs)

    encoding = self.tokenizer(
        input_text,
        return_tensors="pt",
        padding=True,
        truncation=True,
        max_length=max_length,
    )
    input_ids = encoding["input_ids"].to(self.device)
    attention_mask = encoding["attention_mask"].to(self.device)

    with torch.no_grad():
        outputs = model.generate(
            input_ids=input_ids,
            attention_mask=attention_mask,
            **gen_kwargs,
        )

    candidates = self.tokenizer.batch_decode(outputs, skip_special_tokens=True)
    return self._postprocess(
        text, candidates, num_candidates, estimator, min_semantic
    )

paraphrases(texts: List[str], **kwargs) -> List[List[str]]

Generates paraphrases for a batch of texts. Automatically groups questions and sentences to run them through their respective models.

Parameters:

Name Type Description Default
texts List[str]

List of input texts to paraphrase.

required
**kwargs

Generation parameters and control values (same as paraphrase method). - batch_size (int, optional): Chunk size for batch processing.

{}

Returns:

Type Description
List[List[str]]

List of lists of paraphrased strings matching the original text order.

Source code in src/vietquill/generation/controllable_paraphraser/ensemble.py
def paraphrases(self, texts: List[str], **kwargs) -> List[List[str]]:
    """
    Generates paraphrases for a batch of texts.
    Automatically groups questions and sentences to run them through their respective models.

    Args:
        texts: List of input texts to paraphrase.
        **kwargs: Generation parameters and control values (same as paraphrase method).
            - batch_size (int, optional): Chunk size for batch processing.

    Returns:
        List of lists of paraphrased strings matching the original text order.
    """
    if not texts:
        return []

    sentence_indices = []
    question_indices = []
    sentence_inputs = []
    question_inputs = []

    sem_norm, syn_norm, lex_norm = self._resolve_controls(kwargs)
    estimator, min_semantic = self._pop_quality_filter(kwargs)
    batch_size = kwargs.pop("batch_size", None)
    gen_kwargs, max_length, num_candidates = self._prepare_gen_kwargs(kwargs)
    actual_num_return = gen_kwargs["num_return_sequences"]

    for idx, text in enumerate(texts):
        is_question = text.strip().endswith("?")
        input_text = self._format_input_text(text, sem_norm, syn_norm, lex_norm)
        if is_question:
            question_indices.append(idx)
            question_inputs.append(input_text)
        else:
            sentence_indices.append(idx)
            sentence_inputs.append(input_text)

    results = [None] * len(texts)

    def _generate_chunk(inputs, model, indices):
        encoding = self.tokenizer(
            inputs,
            return_tensors="pt",
            padding=True,
            truncation=True,
            max_length=max_length,
        )
        input_ids = encoding["input_ids"].to(self.device)
        attention_mask = encoding["attention_mask"].to(self.device)

        with torch.no_grad():
            outputs = model.generate(
                input_ids=input_ids,
                attention_mask=attention_mask,
                **gen_kwargs,
            )

        decoded = self.tokenizer.batch_decode(outputs, skip_special_tokens=True)

        for i, orig_idx in enumerate(indices):
            start_idx = i * actual_num_return
            end_idx = start_idx + actual_num_return
            candidates = decoded[start_idx:end_idx]
            results[orig_idx] = self._postprocess(
                texts[orig_idx], candidates, num_candidates, estimator, min_semantic
            )

    def _generate_batch(inputs, model, indices):
        step = batch_size or len(inputs)
        for start in range(0, len(inputs), step):
            _generate_chunk(
                inputs[start : start + step],
                model,
                indices[start : start + step],
            )

    _generate_batch(sentence_inputs, self.model_sentence, sentence_indices)
    _generate_batch(question_inputs, self.model_question, question_indices)

    return results

vietquill.generation.controllable_paraphraser.ParaphraseStyle

Bases: Enum

vietquill.generation.fewshot_paraphraser.MimicControl

Bases: BaseModel

Represents a user-defined semantic or stylistic control dimension.

vietquill.generation.fewshot_paraphraser.MimicExample

Bases: BaseModel

Demonstrates an input-output transformation under a specific configuration of controls.

vietquill.generation.fewshot_paraphraser.Mimic

Bases: BaseModel

Reusable transformation specification containing intent, control dimensions, and demonstrations.

validate_example_controls() -> Mimic

Validate that all control names present in examples are declared in controls.

Source code in src/vietquill/generation/fewshot_paraphraser/fewshot_paraphraser.py
@model_validator(mode="after")
def validate_example_controls(self) -> "Mimic":
    """Validate that all control names present in examples are declared in controls."""
    defined_control_names = {c.name for c in self.controls}
    for idx, example in enumerate(self.examples, start=1):
        undefined = set(example.controls.keys()) - defined_control_names
        if undefined:
            raise ValueError(
                f"Example {idx} contains undefined control name(s): {sorted(undefined)}. "
                f"Defined controls are: {sorted(defined_control_names)}"
            )
    return self

vietquill.generation.fewshot_paraphraser.FewshotModelForControllableParaphraseGeneration(client: Optional[Any] = None, model: Optional[str] = None, api_key: Optional[str] = None, base_url: Optional[str] = None, temperature: float = 0.7, max_tokens: Optional[int] = None, top_p: Optional[float] = None, timeout: Optional[float] = None, **extra_default_kwargs)

Few-shot controllable paraphrasing generator for VietQuill using the Mimic paradigm.

The generator infers transformation behavior from demonstration examples and generates paraphrases for new inputs according to a target output control specification. Powered by the OpenAI Python SDK and fully compatible with OpenRouter and any OpenAI-compatible endpoint.

Initialize FewshotModelForControllableParaphraseGeneration.

Parameters:

Name Type Description Default
client Optional[Any]

Pre-configured OpenAI or OpenAI-compatible client instance.

None
model Optional[str]

Model identifier (e.g. 'gpt-4o-mini', 'qwen/qwen-2.5-72b-instruct', etc.). Defaults to 'openai/gpt-4o-mini' on OpenRouter and 'gpt-4o-mini' elsewhere.

None
api_key Optional[str]

API key if client is not directly provided.

None
base_url Optional[str]

Base URL for OpenAI-compatible providers (e.g. 'https://openrouter.ai/api/v1').

None
temperature float

Sampling temperature for generation.

0.7
max_tokens Optional[int]

Maximum tokens to generate in response.

None
top_p Optional[float]

Top-p nucleus sampling parameter.

None
timeout Optional[float]

Request timeout in seconds.

None
**extra_default_kwargs

Additional parameters forwarded to client.chat.completions.create.

{}
Source code in src/vietquill/generation/fewshot_paraphraser/fewshot_paraphraser.py
def __init__(
    self,
    client: Optional[Any] = None,
    model: Optional[str] = None,
    api_key: Optional[str] = None,
    base_url: Optional[str] = None,
    temperature: float = 0.7,
    max_tokens: Optional[int] = None,
    top_p: Optional[float] = None,
    timeout: Optional[float] = None,
    **extra_default_kwargs,
):
    """Initialize FewshotModelForControllableParaphraseGeneration.

    Args:
        client: Pre-configured OpenAI or OpenAI-compatible client instance.
        model: Model identifier (e.g. 'gpt-4o-mini', 'qwen/qwen-2.5-72b-instruct', etc.).
            Defaults to 'openai/gpt-4o-mini' on OpenRouter and 'gpt-4o-mini' elsewhere.
        api_key: API key if client is not directly provided.
        base_url: Base URL for OpenAI-compatible providers (e.g. 'https://openrouter.ai/api/v1').
        temperature: Sampling temperature for generation.
        max_tokens: Maximum tokens to generate in response.
        top_p: Top-p nucleus sampling parameter.
        timeout: Request timeout in seconds.
        **extra_default_kwargs: Additional parameters forwarded to client.chat.completions.create.
    """
    self.temperature = temperature
    self.max_tokens = max_tokens
    self.top_p = top_p
    self.timeout = timeout
    self.extra_default_kwargs = extra_default_kwargs

    if client is not None:
        self.client = client
    else:
        if OpenAI is None:
            raise ImportError(
                "The 'openai' package is required to use FewshotModelForControllableParaphraseGeneration. "
                "Please install it with: pip install openai"
            )
        key = api_key
        if key is None:
            openrouter_key = os.environ.get("OPENROUTER_API_KEY")
            openai_key = os.environ.get("OPENAI_API_KEY")
            if base_url is None:
                # Only fall back to the OpenRouter key when we can route it to OpenRouter.
                if openai_key:
                    key = openai_key
                elif openrouter_key:
                    key = openrouter_key
                    base_url = self.OPENROUTER_BASE_URL
            elif "openrouter" in base_url:
                key = openrouter_key or openai_key
            else:
                key = openai_key or openrouter_key
        self.client = OpenAI(
            api_key=key,
            base_url=base_url,
            timeout=timeout,
        )

    if model is None:
        client_base_url = str(getattr(self.client, "base_url", "") or "")
        model = (
            self.DEFAULT_OPENROUTER_MODEL
            if "openrouter" in client_base_url
            else self.DEFAULT_MODEL
        )
    self.model = model

build_messages(mimic: Mimic, input_text: str, output_control: Dict[str, ControlValue]) -> List[Dict[str, str]]

Construct the Chat Completion messages payload (system and user messages).

Source code in src/vietquill/generation/fewshot_paraphraser/fewshot_paraphraser.py
def build_messages(
    self,
    mimic: Mimic,
    input_text: str,
    output_control: Dict[str, ControlValue],
) -> List[Dict[str, str]]:
    """Construct the Chat Completion messages payload (system and user messages)."""
    prompt = self.build_prompt(mimic, input_text, output_control)
    return [
        {"role": "system", "content": self.SYSTEM_ROLE_PROMPT},
        {"role": "user", "content": prompt},
    ]

build_prompt(mimic: Mimic, input_text: str, output_control: Dict[str, ControlValue]) -> str

Construct the few-shot prompt for text transformation.

Source code in src/vietquill/generation/fewshot_paraphraser/fewshot_paraphraser.py
def build_prompt(
    self,
    mimic: Mimic,
    input_text: str,
    output_control: Dict[str, ControlValue],
) -> str:
    """Construct the few-shot prompt for text transformation."""
    sections = []

    # 1. Intent
    sections.append(f"INTENT:\n{mimic.intent.strip()}")

    # 2. Control definitions
    if mimic.controls:
        ctrl_defs = ["CONTROL DEFINITIONS\n"]
        for ctrl in mimic.controls:
            ctrl_defs.append(f"- {ctrl.name}:\n  {ctrl.description.strip()}\n")
        sections.append("\n".join(ctrl_defs).strip())

    # 3. Few-shot demonstrations
    if mimic.examples:
        demo_sections = []
        for idx, ex in enumerate(mimic.examples, start=1):
            ex_lines = [f"EXAMPLE {idx}\n"]
            if ex.controls:
                ex_lines.append("Control values:")
                for k, v in ex.controls.items():
                    ex_lines.append(f"- {k}: {self._format_control_value(v)}")
                ex_lines.append("")
            ex_lines.append("Input:")
            ex_lines.append(ex.input_text.strip())
            ex_lines.append("\nOutput:")
            ex_lines.append(ex.output_text.strip())
            demo_sections.append("\n".join(ex_lines))
        sections.append("\n\n---\n\n".join(demo_sections))

    # 4. Target task
    target_lines = ["TARGET CONTROL\n"]
    if output_control:
        for k, v in output_control.items():
            target_lines.append(f"- {k}: {self._format_control_value(v)}")
    else:
        target_lines.append("- (default)")

    target_lines.append("\nINPUT:")
    target_lines.append(input_text.strip())
    target_lines.append("\nOUTPUT:")

    sections.append("\n".join(target_lines))

    # 5. Generation constraints
    sections.append(self.GENERATION_CONSTRAINTS)

    return "\n\n====================\n\n".join(sections)

paraphrase(mimic: Mimic, input_text: str, output_control: Dict[str, ControlValue], temperature: Optional[float] = None, max_tokens: Optional[int] = None, **kwargs) -> str

Generate a paraphrase for the input_text adhering to the target output_control.

Parameters:

Name Type Description Default
mimic Mimic

The Mimic specification containing intent, controls, and demonstrations.

required
input_text str

The Vietnamese sentence or paragraph to paraphrase.

required
output_control Dict[str, ControlValue]

Desired control values for the target output.

required
temperature Optional[float]

Optional sampling temperature overriding default.

None
max_tokens Optional[int]

Optional token limit overriding default.

None
**kwargs

Extra parameters passed to client.chat.completions.create.

{}

Returns:

Type Description
str

The generated paraphrase text in Vietnamese.

Raises:

Type Description
ValueError

If input_text is empty or control names are undefined.

Source code in src/vietquill/generation/fewshot_paraphraser/fewshot_paraphraser.py
def paraphrase(
    self,
    mimic: Mimic,
    input_text: str,
    output_control: Dict[str, ControlValue],
    temperature: Optional[float] = None,
    max_tokens: Optional[int] = None,
    **kwargs,
) -> str:
    """Generate a paraphrase for the input_text adhering to the target output_control.

    Args:
        mimic: The Mimic specification containing intent, controls, and demonstrations.
        input_text: The Vietnamese sentence or paragraph to paraphrase.
        output_control: Desired control values for the target output.
        temperature: Optional sampling temperature overriding default.
        max_tokens: Optional token limit overriding default.
        **kwargs: Extra parameters passed to client.chat.completions.create.

    Returns:
        The generated paraphrase text in Vietnamese.

    Raises:
        ValueError: If input_text is empty or control names are undefined.
    """
    # 1-3. Validate input and controls
    self.validate(mimic, input_text, output_control)

    # 4. Construct messages payload
    messages = self.build_messages(mimic, input_text, output_control)

    # Merge generation kwargs
    call_kwargs = dict(self.extra_default_kwargs)
    call_kwargs.update(kwargs)

    temp = temperature if temperature is not None else self.temperature
    tokens = max_tokens if max_tokens is not None else self.max_tokens

    if temp is not None:
        call_kwargs["temperature"] = temp
    if tokens is not None:
        call_kwargs["max_tokens"] = tokens
    if self.top_p is not None and "top_p" not in call_kwargs:
        call_kwargs["top_p"] = self.top_p

    # 5. Send prompt to LLM through OpenAI SDK
    response = self.client.chat.completions.create(
        model=self.model,
        messages=messages,
        **call_kwargs,
    )

    # 6. Extract and return generated text
    raw_output = response.choices[0].message.content or ""
    return self._clean_output(raw_output)

validate(mimic: Mimic, input_text: str, output_control: Dict[str, ControlValue]) -> None

Validate input parameters and verify consistency between controls and definitions.

Raises:

Type Description
ValueError

If input_text is empty or if any control names in examples or output_control are not defined in mimic.controls.

TypeError

If arguments are of incorrect types.

Source code in src/vietquill/generation/fewshot_paraphraser/fewshot_paraphraser.py
def validate(
    self,
    mimic: Mimic,
    input_text: str,
    output_control: Dict[str, ControlValue],
) -> None:
    """Validate input parameters and verify consistency between controls and definitions.

    Raises:
        ValueError: If input_text is empty or if any control names in examples or output_control
            are not defined in mimic.controls.
        TypeError: If arguments are of incorrect types.
    """
    if not isinstance(mimic, Mimic):
        raise TypeError("mimic must be an instance of Mimic.")

    if not isinstance(input_text, str) or not input_text.strip():
        raise ValueError("input_text must be a non-empty string.")

    if not isinstance(output_control, dict):
        raise TypeError("output_control must be a dictionary of control names and values.")

    defined_names = {c.name for c in mimic.controls}

    # Validate example controls
    for idx, example in enumerate(mimic.examples, start=1):
        undefined_example = set(example.controls.keys()) - defined_names
        if undefined_example:
            raise ValueError(
                f"Example {idx} contains undefined control name(s): {sorted(undefined_example)}. "
                f"Defined controls in Mimic are: {sorted(defined_names)}"
            )

    # Validate output_control names
    undefined_output = set(output_control.keys()) - defined_names
    if undefined_output:
        raise ValueError(
            f"output_control contains undefined control name(s): {sorted(undefined_output)}. "
            f"Defined controls in Mimic are: {sorted(defined_names)}"
        )