---
title: "frame.choices@1 · OpenVibe.Services"
canonical: "https://openvibe.services/docs/contracts/frame.choices"
updated: null
---

# frame.choices@1 · OpenVibe.Services

HTML: [frame.choices@1 · OpenVibe.Services](https://openvibe.services/docs/contracts/frame.choices)

# FrameChoices `frame.choices@1`

Generated at 2026-11-13 06:25:48 UTC from [`openvibe-contracts v0.419.0`](https://github.com/OpenVibers/OpenVibe.Contracts/tree/v0.419.0) and [`openvibe-sdk v0.161.0`](https://github.com/OpenVibers/OpenVibe.SDK/tree/v0.161.0).

- Version: 1.0.0
- Owner: network
- Visibility: public
- Status: active
- Compatibility: backward
- Schema: [`https://openvibe.network/contracts/frame/choices.v1.json`](/docs/contracts/frame.choices.json)

frame.choices@1: what a composer's Model sheet shows. GET /api/frame/choices?task=chat|agent|code|image|research answers this. recommended and choice are the resolved model and where it comes from; groups are OpenVibe's models (enriched from OpenVibe.Exchange with the maker, the price per million tokens in and out, the measured speed, the rating and the model's Exchange page), the person's own keys, their computers, and their plans. An 18+ row appears only when the person opted in (adult). A group may carry a note when it has no rows or a part of the answer is unavailable.

## Fields

| Field | Type | Required | Description | Constraints |
| --- | --- | --- | --- | --- |
| `task` | enum | yes |  | - `one of "chat", "agent", "code", "image", "research"` |
| `recommended` | `https://openvibe.network/contracts/frame/choices.v1.json#/$defs/choice` | yes |  |  |
| `choice` | `https://openvibe.network/contracts/frame/choices.v1.json#/$defs/choice` | yes |  |  |
| `adult` | boolean |  | The person may see 18+ models. |  |
| `groups` | array of object | yes |  | - `items: no other fields` |
| `groups[].id` | enum | yes |  | - `one of "openvibe", "keys", "machines", "plans"` |
| `groups[].title` | string | yes |  | - `maxLength 40` |
| `groups[].rows` | `https://openvibe.network/contracts/frame/choices.v1.json#/$defs/row` | yes |  |  |
| `groups[].note` | string |  | Why a group is empty or partial. | - `maxLength 300` |

## Examples

From the contract's own test fixtures: valid ones validate, rejected ones must fail.

 Valid: chat

~~~~
{
  "task": "chat",
  "recommended": {
    "model": "openai/gpt-5-mini",
    "source": "openvibe"
  },
  "choice": {
    "model": "openai/gpt-5-mini",
    "source": "openvibe"
  },
  "adult": false,
  "groups": [
    {
      "id": "openvibe",
      "title": "OpenVibe",
      "rows": [
        {
          "model": "openai/gpt-5-mini",
          "source": "openvibe",
          "name": "GPT-5 Mini",
          "maker": "OpenAI",
          "price": {
            "in": 0.25,
            "out": 2,
            "unit": "USD per 1M tokens"
          },
          "free": false,
          "speed": 750,
          "score": 0.82,
          "url": "https://openvibe.exchange/models/openai%2Fgpt-5-mini"
        },
        {
          "model": "llama3.1:8b",
          "source": "machines",
          "machine": "dev_01J8Z3Q4R5S6T7V8W9X0Y1Z2A3",
          "name": "llama3.1:8b",
          "maker": null,
          "price": null,
          "free": true
        }
      ],
      "note": "Rows are live."
    }
  ]
}
~~~~

Rejected: no-groups

~~~~
{
  "task": "chat",
  "recommended": {
    "model": "openai/gpt-5-mini",
    "source": "openvibe"
  },
  "choice": {
    "model": "openai/gpt-5-mini",
    "source": "openvibe"
  }
}
~~~~

## Validate

~~~~
const contracts = require('openvibe-contracts');
contracts.validate('frame.choices@1', value);   // { valid, errors: [{ path, message }] }
~~~~
