{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://jambonz.org/schema/components/llm-base",
  "title": "LLM Base Properties",
  "description": "Shared properties for llm, s2s, and vendor-specific s2s verb schemas.",
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "An optional unique identifier for this verb instance."
    },
    "vendor": {
      "type": "string",
      "description": "The LLM vendor to use."
    },
    "model": {
      "type": "string",
      "description": "The specific model to use from the vendor.",
      "examples": ["gpt-4o", "claude-sonnet-4-20250514", "gemini-2.0-flash"]
    },
    "auth": {
      "type": "object",
      "description": "Authentication credentials for the LLM vendor API.",
      "properties": {
        "apiKey": {
          "type": "string",
          "description": "The API key for the LLM vendor."
        }
      },
      "additionalProperties": true
    },
    "connectOptions": {
      "type": "object",
      "description": "Additional connection options for the LLM vendor, such as custom base URLs or API versions.",
      "additionalProperties": true
    },
    "llmOptions": {
      "type": "object",
      "description": "Configuration passed to the LLM including the system prompt, temperature, tools/functions, and other model parameters. The structure varies by vendor but typically includes 'messages' (conversation history), 'temperature', 'tools' (function definitions), and 'maxTokens'.",
      "additionalProperties": true,
      "examples": [
        {
          "messages": [
            { "role": "system", "content": "You are a helpful customer service agent for Acme Corp." }
          ],
          "temperature": 0.7
        }
      ]
    },
    "mcpServers": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "The URL of the MCP server."
          },
          "auth": {
            "type": "object",
            "description": "Authentication for the MCP server.",
            "additionalProperties": true
          },
          "roots": {
            "type": "array",
            "items": { "type": "object" },
            "description": "MCP root definitions."
          }
        },
        "required": ["url"]
      },
      "description": "Model Context Protocol servers to connect to. MCP servers provide tools that the LLM can invoke during the conversation."
    },
    "actionHook": {
      "$ref": "actionHook",
      "description": "A webhook invoked when the LLM conversation ends. Receives conversation details and should return the next verbs to execute."
    },
    "eventHook": {
      "$ref": "actionHook",
      "description": "A webhook invoked for real-time events during the LLM conversation (e.g. tool calls, transcription events)."
    },
    "toolHook": {
      "$ref": "actionHook",
      "description": "A webhook invoked when the LLM calls a tool/function. Receives the tool name and arguments, and should return the tool result."
    },
    "handoff": {
      "$ref": "handoff",
      "description": "Declarative transfer-to-human config for s2s verbs. When present, the runtime injects a 'transfer_to_human' tool and runs the packaged transfer choreography when the model calls it."
    },
    "hangup": {
      "$ref": "hangup",
      "description": "Enable the built-in hangup tool for s2s verbs. When present, the runtime injects a 'hangup' tool; when the model calls it, the call is ended with a reason placed in an X-Reason SIP header (LLM-supplied reason wins, falling back to hangup.reason)."
    },
    "events": {
      "type": "array",
      "items": { "type": "string" },
      "description": "List of event types to receive via the eventHook."
    },
    "responseTimeoutMs": {
      "type": "integer",
      "minimum": 1,
      "description": "Opt-in per-response stall watchdog (s2s vendors that support app-driven turn-taking, e.g. openai). Milliseconds to wait for a solicited response to be acknowledged before declaring a stall. When the timer expires a '{type:\"response.timeout\"}' event is sent to the eventHook. Omit or set to 0 to disable (the default)."
    },
    "cancelOnResponseTimeout": {
      "type": "boolean",
      "default": false,
      "description": "When a response stall is detected (see responseTimeoutMs), cancel the in-progress server-side response so the next turn does not collide with an active response. Has no effect unless responseTimeoutMs is set."
    },
    "cancelOnBargeIn": {
      "type": "boolean",
      "default": false,
      "description": "On caller barge-in, cancel the in-progress server-side response. Intended for app-driven turn-taking configurations where the vendor does not cancel the response itself (server-VAD configurations are unaffected)."
    }
  }
}
