from __future__ import annotations

from collections.abc import Mapping
from typing import Any, TypeVar, BinaryIO, TextIO, TYPE_CHECKING, Generator

from attrs import define as _attrs_define
from attrs import field as _attrs_field

from ..types import UNSET, Unset

from ..models.agents_config_agent_api_type import AgentsConfigAgentApiType
from ..types import UNSET, Unset
from typing import cast

if TYPE_CHECKING:
  from ..models.run_code_config import RunCodeConfig
  from ..models.web_search_config import WebSearchConfig





T = TypeVar("T", bound="AgentsConfig")



@_attrs_define
class AgentsConfig:
    r""" Configuration for agent workflow.

        Attributes:
            enabled (bool | Unset): Whether to use agents mode for queries. Default: True.
            human_in_the_loop (bool | Unset): Enable the ask_user tool, allowing the agent to pause and ask the user a
                question with options. Default: False.
            web_search (WebSearchConfig | Unset): Configuration for web search tools.
            run_code (RunCodeConfig | Unset): Configuration for the run_code agent tool (Docker sandbox).
            mcp_tools (list[str] | Unset): MCP tool names to enable from LiteLLM (e.g. ['read_wiki_contents',
                'ask_question']).
            planning_enabled (bool | Unset): Include the create_plan tool so the agent can generate a research plan when it
                deems a task complex enough. Plan approval is configured on PlanningLLM. Default: False.
            suggested_queries (bool | Unset): Generate a single suggested follow-up query after each response. Default:
                False.
            artifacts_enabled (bool | Unset): Enable the create_artifact tool for generating standalone documents
                (summaries, reports, drafts) displayed in a side panel. Default: False.
            vision_enabled (bool | Unset): Enable the view_document_pages tool for visual inspection of document pages
                (figures, charts, scanned pages) using a vision-capable model. Default: False.
            personal_agent (bool | Unset): Enable the personal_agent tool for delegating tasks to an external agent via
                OpenClaw. Default: False.
            memory_enabled (bool | Unset): Run memory synthesis (skills, memories) after agent queries. Default: False.
            skills_enabled (bool | Unset): Enable the skills system: /skill (create) and /<skill-name> (invoke) commands.
                Default: False.
            skill_creation (bool | Unset): Transient flag set by /skill command to enable the save_skill agent tool.
                Default: False.
            review_enabled (bool | Unset): Enable ReviewLLM to review agent draft answers against source material before
                sending to the user. Uses the reasoning model for higher quality. Default: False.
            persona (str | Unset): Agent persona prepended to the system prompt. Customise to change the agent's identity
                and tone. Default: 'You are ARBI, an AI assistant created by ARBI CITY. Be proactive, helpful and
                professional.'.
            agent_model_name (str | Unset): The name of the model to be used for the agent decision-making. Default: 'auto'.
            agent_api_type (AgentsConfigAgentApiType | Unset): The inference type for the agent LLM (local or remote).
                Default: AgentsConfigAgentApiType.REMOTE.
            llm_agent_temperature (float | Unset): Temperature value for agent LLM. Default: 1.0.
            agent_max_tokens (int | Unset): Maximum tokens for the agent LLM response. Default: 10000.
            enable_thinking (bool | Unset): Enable reasoning/thinking mode for the agentic loop LLM. Default: False.
            agent_max_iterations (int | Unset): Maximum number of tool call iterations before forcing an answer. Default:
                20.
            agent_max_parallel_tool_calls (int | Unset): Maximum number of tool calls the agent can make in a single
                iteration. Extra calls beyond this limit are silently dropped. Default: 10.
            agent_history_char_threshold (int | Unset): Char threshold for conversation history injected into the agent
                context. Below: verbatim user/assistant pairs. Above: summarized by SummariseLLM. Default: 8000.
            agent_system_prompt (str | Unset): System prompt for the agent. Dynamic context is appended automatically.
                Default: 'Answer any question the user asks. You have access to workspace documents — use them when relevant.
                When no documents are relevant, answer from your own knowledge. Always provide a helpful answer.\n\nIMPORTANT:
                Never say \\"I don\'t have information in the available documents\\" or refuse to answer. If documents are not
                relevant, just answer the question directly from your own knowledge.\n\nWhen using documents, retrieve actual
                document content — do not rely on metadata or headings alone. Cross-check information across sources. Not all
                assertions in sources are factual — some are arguments by a party. Attribute claims to their source where
                parties disagree.\n\nDOCUMENT INDEX:\nThe \\"Workspace Documents\\" table in your system prompt already lists
                every document with its Id, Title, Date, Pages, and Subject. Use this table directly when the user asks to list,
                catalogue, or tabulate documents — do NOT call any tools for that. Only use tools when you need to read or
                search document content.\n\nDOCUMENT STRATEGY:\n- For listing/cataloguing documents: use the document index
                already in your prompt — no tool calls needed.\n- For overview/summary requests: use get_table_of_contents
                first, then read key sections with read_document.\n- For specific questions: use search_inside_documents to find
                relevant sections, then read those sections with read_document.\n- For translation, deep analysis, or detailed
                summaries of a single document: use read_document with no ranges to load the complete text.\n- For extracting
                structured findings from passages: use read_document with digest=true or search_inside_documents with
                digest=true.\n- Never search for meta-queries like \\"summary\\", \\"overview\\", or \\"all documents\\" —
                search for the actual subject matter.\n\nFORMATTING:\n- Start your answer directly with the content — never open
                with a description of what you searched for or read (e.g. never write \\"Searching for X\\", \\"Reading Y\\",
                \\"I found Z documents\\" as the first lines of an answer).\n- Use markdown: headings, bold, bullet points,
                numbered lists\n- Main conclusion first, followed by supporting points\n- Bullet points for lists of discrete
                items\n- Numbering for sequential steps or prioritized items\n- Bold text for critical conclusions or decision
                points\n\nEFFICIENCY:\n1. Before each step, review the results of prior steps. Adapt your approach based on what
                you learned — if a call was rejected or returned poor results, adjust your strategy.\n2. Call multiple tools in
                parallel when a query has several aspects.\n3. NEVER repeat a tool call with the same arguments.\n4. Use
                targeted queries and narrow page ranges per retrieval call.\n5. Stop when you have sufficient evidence — do not
                over-research.'.
     """

    enabled: bool | Unset = True
    human_in_the_loop: bool | Unset = False
    web_search: WebSearchConfig | Unset = UNSET
    run_code: RunCodeConfig | Unset = UNSET
    mcp_tools: list[str] | Unset = UNSET
    planning_enabled: bool | Unset = False
    suggested_queries: bool | Unset = False
    artifacts_enabled: bool | Unset = False
    vision_enabled: bool | Unset = False
    personal_agent: bool | Unset = False
    memory_enabled: bool | Unset = False
    skills_enabled: bool | Unset = False
    skill_creation: bool | Unset = False
    review_enabled: bool | Unset = False
    persona: str | Unset = 'You are ARBI, an AI assistant created by ARBI CITY. Be proactive, helpful and professional.'
    agent_model_name: str | Unset = 'auto'
    agent_api_type: AgentsConfigAgentApiType | Unset = AgentsConfigAgentApiType.REMOTE
    llm_agent_temperature: float | Unset = 1.0
    agent_max_tokens: int | Unset = 10000
    enable_thinking: bool | Unset = False
    agent_max_iterations: int | Unset = 20
    agent_max_parallel_tool_calls: int | Unset = 10
    agent_history_char_threshold: int | Unset = 8000
    agent_system_prompt: str | Unset = 'Answer any question the user asks. You have access to workspace documents — use them when relevant. When no documents are relevant, answer from your own knowledge. Always provide a helpful answer.\n\nIMPORTANT: Never say \\"I don\'t have information in the available documents\\" or refuse to answer. If documents are not relevant, just answer the question directly from your own knowledge.\n\nWhen using documents, retrieve actual document content — do not rely on metadata or headings alone. Cross-check information across sources. Not all assertions in sources are factual — some are arguments by a party. Attribute claims to their source where parties disagree.\n\nDOCUMENT INDEX:\nThe \\"Workspace Documents\\" table in your system prompt already lists every document with its Id, Title, Date, Pages, and Subject. Use this table directly when the user asks to list, catalogue, or tabulate documents — do NOT call any tools for that. Only use tools when you need to read or search document content.\n\nDOCUMENT STRATEGY:\n- For listing/cataloguing documents: use the document index already in your prompt — no tool calls needed.\n- For overview/summary requests: use get_table_of_contents first, then read key sections with read_document.\n- For specific questions: use search_inside_documents to find relevant sections, then read those sections with read_document.\n- For translation, deep analysis, or detailed summaries of a single document: use read_document with no ranges to load the complete text.\n- For extracting structured findings from passages: use read_document with digest=true or search_inside_documents with digest=true.\n- Never search for meta-queries like \\"summary\\", \\"overview\\", or \\"all documents\\" — search for the actual subject matter.\n\nFORMATTING:\n- Start your answer directly with the content — never open with a description of what you searched for or read (e.g. never write \\"Searching for X\\", \\"Reading Y\\", \\"I found Z documents\\" as the first lines of an answer).\n- Use markdown: headings, bold, bullet points, numbered lists\n- Main conclusion first, followed by supporting points\n- Bullet points for lists of discrete items\n- Numbering for sequential steps or prioritized items\n- Bold text for critical conclusions or decision points\n\nEFFICIENCY:\n1. Before each step, review the results of prior steps. Adapt your approach based on what you learned — if a call was rejected or returned poor results, adjust your strategy.\n2. Call multiple tools in parallel when a query has several aspects.\n3. NEVER repeat a tool call with the same arguments.\n4. Use targeted queries and narrow page ranges per retrieval call.\n5. Stop when you have sufficient evidence — do not over-research.'
    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)





    def to_dict(self) -> dict[str, Any]:
        from ..models.web_search_config import WebSearchConfig
        from ..models.run_code_config import RunCodeConfig
        enabled = self.enabled

        human_in_the_loop = self.human_in_the_loop

        web_search: dict[str, Any] | Unset = UNSET
        if not isinstance(self.web_search, Unset):
            web_search = self.web_search.to_dict()

        run_code: dict[str, Any] | Unset = UNSET
        if not isinstance(self.run_code, Unset):
            run_code = self.run_code.to_dict()

        mcp_tools: list[str] | Unset = UNSET
        if not isinstance(self.mcp_tools, Unset):
            mcp_tools = self.mcp_tools



        planning_enabled = self.planning_enabled

        suggested_queries = self.suggested_queries

        artifacts_enabled = self.artifacts_enabled

        vision_enabled = self.vision_enabled

        personal_agent = self.personal_agent

        memory_enabled = self.memory_enabled

        skills_enabled = self.skills_enabled

        skill_creation = self.skill_creation

        review_enabled = self.review_enabled

        persona = self.persona

        agent_model_name = self.agent_model_name

        agent_api_type: str | Unset = UNSET
        if not isinstance(self.agent_api_type, Unset):
            agent_api_type = self.agent_api_type.value


        llm_agent_temperature = self.llm_agent_temperature

        agent_max_tokens = self.agent_max_tokens

        enable_thinking = self.enable_thinking

        agent_max_iterations = self.agent_max_iterations

        agent_max_parallel_tool_calls = self.agent_max_parallel_tool_calls

        agent_history_char_threshold = self.agent_history_char_threshold

        agent_system_prompt = self.agent_system_prompt


        field_dict: dict[str, Any] = {}
        field_dict.update(self.additional_properties)
        field_dict.update({
        })
        if enabled is not UNSET:
            field_dict["ENABLED"] = enabled
        if human_in_the_loop is not UNSET:
            field_dict["HUMAN_IN_THE_LOOP"] = human_in_the_loop
        if web_search is not UNSET:
            field_dict["WEB_SEARCH"] = web_search
        if run_code is not UNSET:
            field_dict["RUN_CODE"] = run_code
        if mcp_tools is not UNSET:
            field_dict["MCP_TOOLS"] = mcp_tools
        if planning_enabled is not UNSET:
            field_dict["PLANNING_ENABLED"] = planning_enabled
        if suggested_queries is not UNSET:
            field_dict["SUGGESTED_QUERIES"] = suggested_queries
        if artifacts_enabled is not UNSET:
            field_dict["ARTIFACTS_ENABLED"] = artifacts_enabled
        if vision_enabled is not UNSET:
            field_dict["VISION_ENABLED"] = vision_enabled
        if personal_agent is not UNSET:
            field_dict["PERSONAL_AGENT"] = personal_agent
        if memory_enabled is not UNSET:
            field_dict["MEMORY_ENABLED"] = memory_enabled
        if skills_enabled is not UNSET:
            field_dict["SKILLS_ENABLED"] = skills_enabled
        if skill_creation is not UNSET:
            field_dict["SKILL_CREATION"] = skill_creation
        if review_enabled is not UNSET:
            field_dict["REVIEW_ENABLED"] = review_enabled
        if persona is not UNSET:
            field_dict["PERSONA"] = persona
        if agent_model_name is not UNSET:
            field_dict["AGENT_MODEL_NAME"] = agent_model_name
        if agent_api_type is not UNSET:
            field_dict["AGENT_API_TYPE"] = agent_api_type
        if llm_agent_temperature is not UNSET:
            field_dict["LLM_AGENT_TEMPERATURE"] = llm_agent_temperature
        if agent_max_tokens is not UNSET:
            field_dict["AGENT_MAX_TOKENS"] = agent_max_tokens
        if enable_thinking is not UNSET:
            field_dict["ENABLE_THINKING"] = enable_thinking
        if agent_max_iterations is not UNSET:
            field_dict["AGENT_MAX_ITERATIONS"] = agent_max_iterations
        if agent_max_parallel_tool_calls is not UNSET:
            field_dict["AGENT_MAX_PARALLEL_TOOL_CALLS"] = agent_max_parallel_tool_calls
        if agent_history_char_threshold is not UNSET:
            field_dict["AGENT_HISTORY_CHAR_THRESHOLD"] = agent_history_char_threshold
        if agent_system_prompt is not UNSET:
            field_dict["AGENT_SYSTEM_PROMPT"] = agent_system_prompt

        return field_dict



    @classmethod
    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
        from ..models.run_code_config import RunCodeConfig
        from ..models.web_search_config import WebSearchConfig
        d = dict(src_dict)
        enabled = d.pop("ENABLED", UNSET)

        human_in_the_loop = d.pop("HUMAN_IN_THE_LOOP", UNSET)

        _web_search = d.pop("WEB_SEARCH", UNSET)
        web_search: WebSearchConfig | Unset
        if isinstance(_web_search,  Unset):
            web_search = UNSET
        else:
            web_search = WebSearchConfig.from_dict(_web_search)




        _run_code = d.pop("RUN_CODE", UNSET)
        run_code: RunCodeConfig | Unset
        if isinstance(_run_code,  Unset):
            run_code = UNSET
        else:
            run_code = RunCodeConfig.from_dict(_run_code)




        mcp_tools = cast(list[str], d.pop("MCP_TOOLS", UNSET))


        planning_enabled = d.pop("PLANNING_ENABLED", UNSET)

        suggested_queries = d.pop("SUGGESTED_QUERIES", UNSET)

        artifacts_enabled = d.pop("ARTIFACTS_ENABLED", UNSET)

        vision_enabled = d.pop("VISION_ENABLED", UNSET)

        personal_agent = d.pop("PERSONAL_AGENT", UNSET)

        memory_enabled = d.pop("MEMORY_ENABLED", UNSET)

        skills_enabled = d.pop("SKILLS_ENABLED", UNSET)

        skill_creation = d.pop("SKILL_CREATION", UNSET)

        review_enabled = d.pop("REVIEW_ENABLED", UNSET)

        persona = d.pop("PERSONA", UNSET)

        agent_model_name = d.pop("AGENT_MODEL_NAME", UNSET)

        _agent_api_type = d.pop("AGENT_API_TYPE", UNSET)
        agent_api_type: AgentsConfigAgentApiType | Unset
        if isinstance(_agent_api_type,  Unset):
            agent_api_type = UNSET
        else:
            agent_api_type = AgentsConfigAgentApiType(_agent_api_type)




        llm_agent_temperature = d.pop("LLM_AGENT_TEMPERATURE", UNSET)

        agent_max_tokens = d.pop("AGENT_MAX_TOKENS", UNSET)

        enable_thinking = d.pop("ENABLE_THINKING", UNSET)

        agent_max_iterations = d.pop("AGENT_MAX_ITERATIONS", UNSET)

        agent_max_parallel_tool_calls = d.pop("AGENT_MAX_PARALLEL_TOOL_CALLS", UNSET)

        agent_history_char_threshold = d.pop("AGENT_HISTORY_CHAR_THRESHOLD", UNSET)

        agent_system_prompt = d.pop("AGENT_SYSTEM_PROMPT", UNSET)

        agents_config = cls(
            enabled=enabled,
            human_in_the_loop=human_in_the_loop,
            web_search=web_search,
            run_code=run_code,
            mcp_tools=mcp_tools,
            planning_enabled=planning_enabled,
            suggested_queries=suggested_queries,
            artifacts_enabled=artifacts_enabled,
            vision_enabled=vision_enabled,
            personal_agent=personal_agent,
            memory_enabled=memory_enabled,
            skills_enabled=skills_enabled,
            skill_creation=skill_creation,
            review_enabled=review_enabled,
            persona=persona,
            agent_model_name=agent_model_name,
            agent_api_type=agent_api_type,
            llm_agent_temperature=llm_agent_temperature,
            agent_max_tokens=agent_max_tokens,
            enable_thinking=enable_thinking,
            agent_max_iterations=agent_max_iterations,
            agent_max_parallel_tool_calls=agent_max_parallel_tool_calls,
            agent_history_char_threshold=agent_history_char_threshold,
            agent_system_prompt=agent_system_prompt,
        )


        agents_config.additional_properties = d
        return agents_config

    @property
    def additional_keys(self) -> list[str]:
        return list(self.additional_properties.keys())

    def __getitem__(self, key: str) -> Any:
        return self.additional_properties[key]

    def __setitem__(self, key: str, value: Any) -> None:
        self.additional_properties[key] = value

    def __delitem__(self, key: str) -> None:
        del self.additional_properties[key]

    def __contains__(self, key: str) -> bool:
        return key in self.additional_properties
