{
  "openapi": "3.1.0",
  "info": {
    "title": "НейроВася — текстовый шлюз",
    "version": "0.2.0-local",
    "description": "Локальный серверный API: ключи НейроВаси, бюджеты и проксирование текстовых Chat Completions в AITunnel. Реальные ответы требуют серверного ключа и баланса AITunnel. Платежи и многопользовательская авторизация не подключены."
  },
  "servers": [
    {
      "url": "http://127.0.0.1:4175/v1",
      "description": "Работающий локальный шлюз; внешний провайдер требует настройки владельцем"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/chat/completions": {
      "post": {
        "operationId": "createChatCompletion",
        "summary": "Текстовый ответ через серверный шлюз",
        "description": "Возвращает JSON или SSE при stream=true. Вложения, tools и дополнительные платные инструменты не поддерживаются. При неизвестной стоимости резерв бюджета удерживается до сверки.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChatRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Текстовый ответ",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatResponse"
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Неподдерживаемые параметры или модель"
          },
          "401": {
            "description": "Ключ не прошёл авторизацию"
          },
          "402": {
            "description": "Недостаточный бюджет ключа"
          },
          "403": {
            "description": "Ключ приостановлен или отозван"
          },
          "413": {
            "description": "Превышен размер запроса"
          },
          "429": {
            "description": "Превышен лимит"
          },
          "502": {
            "description": "Ошибка или неизвестная стоимость ответа"
          },
          "503": {
            "description": "Провайдер не настроен или недоступен"
          }
        }
      }
    },
    "/models": {
      "get": {
        "operationId": "listModels",
        "summary": "Разрешённые модели шлюза",
        "responses": {
          "200": {
            "description": "OpenAI-совместимый список наших идентификаторов"
          },
          "401": {
            "description": "Неверный ключ"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Ключ НейроВаси nv_live_… . Не используйте ключ AITunnel или старый демонстрационный nv-demo-ключ."
      }
    },
    "schemas": {
      "Message": {
        "type": "object",
        "required": [
          "role",
          "content"
        ],
        "properties": {
          "role": {
            "type": "string",
            "enum": [
              "system",
              "user",
              "assistant"
            ]
          },
          "content": {
            "type": "string"
          }
        },
        "additionalProperties": false
      },
      "ChatRequest": {
        "type": "object",
        "required": [
          "model",
          "messages"
        ],
        "additionalProperties": false,
        "properties": {
          "model": {
            "type": "string",
            "description": "Рабочие идентификаторы будут определены при подключении провайдера"
          },
          "messages": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/Message"
            }
          },
          "temperature": {
            "type": "number",
            "minimum": 0,
            "maximum": 2,
            "description": "Поддержка и диапазон зависят от модели"
          },
          "max_tokens": {
            "type": "integer",
            "minimum": 1,
            "maximum": 4096
          },
          "stream": {
            "type": "boolean",
            "default": false
          },
          "stream_options": {
            "type": "object",
            "properties": {
              "include_usage": {
                "type": "boolean"
              }
            },
            "additionalProperties": false
          }
        }
      },
      "ChatResponse": {
        "type": "object",
        "required": [
          "id",
          "model",
          "choices"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "model": {
            "type": "string"
          },
          "choices": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "index",
                "message",
                "finish_reason"
              ],
              "properties": {
                "index": {
                  "type": "integer"
                },
                "message": {
                  "$ref": "#/components/schemas/Message"
                },
                "finish_reason": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              }
            }
          },
          "usage": {
            "type": "object",
            "properties": {
              "prompt_tokens": {
                "type": "integer"
              },
              "completion_tokens": {
                "type": "integer"
              },
              "total_tokens": {
                "type": "integer"
              }
            }
          }
        }
      }
    }
  }
}
