# coding: utf-8

"""
    Mixpeek API

    This is the Mixpeek API, providing access to various endpoints for data processing and retrieval.

    The version of the OpenAPI document: 1.3.0
    Contact: info@mixpeek.com
    Generated by OpenAPI Generator (https://openapi-generator.tech)

    Do not edit the class manually.
"""  # noqa: E501


from __future__ import annotations
import pprint
import re  # noqa: F401
import json

from pydantic import BaseModel, ConfigDict, Field, StrictBool, StrictStr, field_validator
from typing import Any, ClassVar, Dict, List, Optional, Union
from typing_extensions import Annotated
from mixpeek.models.llm_provider import LLMProvider
from typing import Optional, Set
from typing_extensions import Self

class StageParamsSummarize(BaseModel):
    """
    Configuration for multi-document summarization.  **Stage Category**: REDUCE  **Transformation**: N documents → 1 document (or N → M with group_by)  **Purpose**: Condense multiple documents into a single summary using an LLM. Unlike llm_enrich which processes each document independently, Summarize provides all documents to the LLM in a single call, enabling cross-document synthesis and comparison.  **When to Use**:     - Generate a single answer from search results (RAG output)     - Create executive summaries from multiple sources     - Synthesize information that spans multiple documents     - Reduce result set to key findings  **When NOT to Use**:     - Adding fields to each document (use llm_enrich instead)     - Simple filtering based on content (use llm_filter instead)     - When you need to preserve individual documents  **Common Pipeline Position**: feature_search → rerank → summarize  **Template Variables**:     - `{{DOCUMENTS}}`: Formatted list of all documents (required in prompt)     - `{{DOC_COUNT}}`: Number of documents being summarized     - `{{INPUT.*}}`: Access query inputs     - `{{CONTEXT.*}}`: Access execution context  Examples:     Basic summarization:         ```json         {             \"prompt\": \"Summarize these {{DOC_COUNT}} search results:\\n\\n{{DOCUMENTS}}\",             \"provider\": \"google\",             \"model_name\": \"gemini-2.0-flash\"         }         ```      Question-answering from search results:         ```json         {             \"prompt\": \"Answer this question: {{INPUT.question}}\\n\\nBased on these documents:\\n{{DOCUMENTS}}\",             \"provider\": \"openai\",             \"model_name\": \"gpt-4o\",             \"include_sources\": true         }         ```      Per-category summarization:         ```json         {             \"prompt\": \"Summarize documents about {{GROUP_VALUE}}:\\n\\n{{DOCUMENTS}}\",             \"provider\": \"openai\",             \"model_name\": \"gpt-4o-mini\",             \"group_by\": \"metadata.category\"         }         ```
    """ # noqa: E501
    prompt: Optional[StrictStr] = Field(default='''Summarize the following {{DOC_COUNT}} documents concisely:

{{DOCUMENTS}}''', description="REQUIRED. Prompt template for the LLM. Must include {{DOCUMENTS}} placeholder.   Available placeholders: - {{DOCUMENTS}}: Formatted list of all documents - {{DOC_COUNT}}: Number of documents - {{GROUP_VALUE}}: Current group value (when using group_by) - {{INPUT.*}}: Query input values - {{CONTEXT.*}}: Execution context")
    provider: Optional[LLMProvider] = Field(default=None, description="LLM provider to use. Supported providers: - openai: GPT models (GPT-4o, GPT-4o-mini) - google: Gemini models (Gemini 2.0 Flash) - anthropic: Claude models (Claude 3.5 Sonnet/Haiku)  If not specified, defaults to 'google'. Can be auto-inferred from model_name.")
    model_name: Optional[StrictStr] = Field(default='null', description="Specific LLM model to use. If not specified, uses provider default. Examples: gemini-2.0-flash, gpt-4o-mini, gpt-4o")
    inference_name: Optional[StrictStr] = Field(default=None, description="DEPRECATED: Use 'provider' and 'model_name' instead. Legacy format: 'provider:model' (e.g., 'gemini:gemini-2.0-flash'). Kept for backward compatibility only.")
    document_template: Optional[StrictStr] = Field(default='''[{{INDEX}}] {{DOC.content}}
''', description="OPTIONAL. Template for formatting each document in {{DOCUMENTS}}. Default: '[{{INDEX}}] {{DOC.content}}\\n'.   Available placeholders: - {{INDEX}}: 1-based document index - {{DOC.*}}: Any document field (e.g., {{DOC.content}}, {{DOC.metadata.title}})")
    content_field: Optional[StrictStr] = Field(default='content', description="OPTIONAL. Primary field to extract content from each document. Used when {{DOC.content}} is referenced in document_template. Supports dot notation for nested fields.")
    group_by: Optional[StrictStr] = Field(default='null', description="OPTIONAL. Field to group documents by before summarization. When set, creates one summary per unique group value (N→M transformation). When not set, creates one summary for all documents (N→1 transformation).   Use cases: - 'metadata.category': One summary per category - 'metadata.source': One summary per source - 'metadata.date': One summary per date")
    output_field: Optional[StrictStr] = Field(default='summary', description="OPTIONAL. Field name for the summary in the output document. Default: 'summary'.")
    include_sources: Optional[StrictBool] = Field(default=True, description="OPTIONAL. Include source document IDs in output. When true, adds 'source_document_ids' field to output. Useful for citation and attribution.")
    include_metadata: Optional[StrictBool] = Field(default=True, description="OPTIONAL. Include metadata about summarization in output. Adds 'document_count', 'tokens_used', etc.")
    max_input_tokens: Optional[Annotated[int, Field(le=128000, strict=True, ge=100)]] = Field(default=8000, description="OPTIONAL. Maximum tokens to use for input documents. Documents exceeding this limit are truncated using truncation_strategy. Default: 8000 (safe for most models).")
    truncation_strategy: Optional[StrictStr] = Field(default='drop_last', description="OPTIONAL. How to handle documents exceeding max_input_tokens.   Strategies: - 'drop_last': Include documents in order until limit, drop remaining - 'truncate_each': Give each document equal token budget, truncate individually - 'smart': Prioritize by relevance score, truncate lower-scored documents first")
    temperature: Optional[Union[Annotated[float, Field(le=2.0, strict=True, ge=0.0)], Annotated[int, Field(le=2, strict=True, ge=0)]]] = Field(default=0.3, description="OPTIONAL. LLM temperature for summary generation. Lower values (0.1-0.3) produce more focused, deterministic summaries. Higher values (0.7-1.0) produce more creative, varied summaries. Default: 0.3 (factual summarization).")
    max_output_tokens: Optional[Annotated[int, Field(le=16000, strict=True, ge=10)]] = Field(default=1024, description="OPTIONAL. Maximum tokens for the summary output. Default: 1024.")
    output_schema: Optional[Dict[str, Any]] = Field(default=None, description="OPTIONAL. JSON schema for structured output. When provided, LLM output is parsed as JSON matching this schema.")
    __properties: ClassVar[List[str]] = ["prompt", "provider", "model_name", "inference_name", "document_template", "content_field", "group_by", "output_field", "include_sources", "include_metadata", "max_input_tokens", "truncation_strategy", "temperature", "max_output_tokens", "output_schema"]

    @field_validator('truncation_strategy')
    def truncation_strategy_validate_enum(cls, value):
        """Validates the enum"""
        if value is None:
            return value

        if value not in set(['drop_last', 'truncate_each', 'smart']):
            raise ValueError("must be one of enum values ('drop_last', 'truncate_each', 'smart')")
        return value

    model_config = ConfigDict(
        populate_by_name=True,
        validate_assignment=True,
        protected_namespaces=(),
    )


    def to_str(self) -> str:
        """Returns the string representation of the model using alias"""
        return pprint.pformat(self.model_dump(by_alias=True))

    def to_json(self) -> str:
        """Returns the JSON representation of the model using alias"""
        # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
        return json.dumps(self.to_dict())

    @classmethod
    def from_json(cls, json_str: str) -> Optional[Self]:
        """Create an instance of StageParamsSummarize from a JSON string"""
        return cls.from_dict(json.loads(json_str))

    def to_dict(self) -> Dict[str, Any]:
        """Return the dictionary representation of the model using alias.

        This has the following differences from calling pydantic's
        `self.model_dump(by_alias=True)`:

        * `None` is only added to the output dict for nullable fields that
          were set at model initialization. Other fields with value `None`
          are ignored.
        """
        excluded_fields: Set[str] = set([
        ])

        _dict = self.model_dump(
            by_alias=True,
            exclude=excluded_fields,
            exclude_none=True,
        )
        return _dict

    @classmethod
    def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
        """Create an instance of StageParamsSummarize from a dict"""
        if obj is None:
            return None

        if not isinstance(obj, dict):
            return cls.model_validate(obj)

        _obj = cls.model_validate({
            "prompt": obj.get("prompt") if obj.get("prompt") is not None else '''Summarize the following {{DOC_COUNT}} documents concisely:

{{DOCUMENTS}}''',
            "provider": obj.get("provider"),
            "model_name": obj.get("model_name") if obj.get("model_name") is not None else 'null',
            "inference_name": obj.get("inference_name"),
            "document_template": obj.get("document_template") if obj.get("document_template") is not None else '''[{{INDEX}}] {{DOC.content}}
''',
            "content_field": obj.get("content_field") if obj.get("content_field") is not None else 'content',
            "group_by": obj.get("group_by") if obj.get("group_by") is not None else 'null',
            "output_field": obj.get("output_field") if obj.get("output_field") is not None else 'summary',
            "include_sources": obj.get("include_sources") if obj.get("include_sources") is not None else True,
            "include_metadata": obj.get("include_metadata") if obj.get("include_metadata") is not None else True,
            "max_input_tokens": obj.get("max_input_tokens") if obj.get("max_input_tokens") is not None else 8000,
            "truncation_strategy": obj.get("truncation_strategy") if obj.get("truncation_strategy") is not None else 'drop_last',
            "temperature": obj.get("temperature") if obj.get("temperature") is not None else 0.3,
            "max_output_tokens": obj.get("max_output_tokens") if obj.get("max_output_tokens") is not None else 1024,
            "output_schema": obj.get("output_schema")
        })
        return _obj


