---
name: eapil-llmapi-structured
description: Guidance for EAPIL LLM API/OpenAI-compatible GPT-5.5 structured JSON output calls through /v1/responses or /v1/chat/completions, especially vision labeling, strict JSON schema, reasoning_effort, token/latency diagnostics, and gateway troubleshooting. Use when building, debugging, or reviewing structured-output requests to EAPIL LLM API relay domains such as api-n-cd.playones.com or similar OpenAI-compatible relays.
---

# EAPIL LLMAPI Structured Output

Use this skill when implementing or debugging structured JSON calls against the EAPIL LLM API OpenAI-compatible gateway.

## Default Choice

Prefer `/v1/responses` with strict JSON schema for GPT-5.5 teacher labeling.

Keep `/v1/chat/completions` as a fallback for compatibility testing, not the default, unless live smoke tests prove it is cheaper and faster for the same schema.

## Known Good Responses Shape

Use this shape for low-latency structured output:

```json
{
  "model": "gpt-5.5",
  "input": [
    {
      "role": "user",
      "content": [
        { "type": "input_text", "text": "只输出一个 JSON object，不要 markdown。" },
        { "type": "input_image", "image_url": "data:image/jpeg;base64,...", "detail": "low" }
      ]
    }
  ],
  "text": {
    "format": {
      "type": "json_schema",
      "name": "task_schema_name",
      "strict": true,
      "schema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "ok": { "type": "boolean" },
          "score": { "type": "number", "minimum": 0, "maximum": 1 },
          "label": { "type": "string", "maxLength": 64 }
        },
        "required": ["ok", "score", "label"]
      }
    }
  },
  "reasoning": { "effort": "none" },
  "truncation": "disabled",
  "max_output_tokens": 700
}
```

Call `POST {base_url}/responses`, where `base_url` may already include `/v1`.

## Chat Fallback Shape

Use this only for fallback comparison:

```json
{
  "model": "gpt-5.5",
  "messages": [
    {
      "role": "user",
      "content": [
        { "type": "text", "text": "只输出一个 JSON object，不要 markdown。" },
        { "type": "image_url", "image_url": { "url": "data:image/jpeg;base64,...", "detail": "low" } }
      ]
    }
  ],
  "response_format": {
    "type": "json_schema",
    "json_schema": {
      "name": "task_schema_name",
      "strict": true,
      "schema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "ok": { "type": "boolean" },
          "score": { "type": "number", "minimum": 0, "maximum": 1 },
          "label": { "type": "string", "maxLength": 64 }
        },
        "required": ["ok", "score", "label"]
      }
    }
  },
  "reasoning_effort": "none",
  "temperature": 0,
  "max_tokens": 700
}
```

Call `POST {base_url}/chat/completions`.

## Schema Rules

- Keep schemas compact. Prefer arrays of present tags over objects containing every boolean flag.
- Use `additionalProperties: false` and `required` for every field in strict schemas.
- Bound free text with `maxLength`; keep notes short.
- Use enum arrays for tags:

```json
{
  "negative_tags": {
    "type": "array",
    "maxItems": 8,
    "items": {
      "type": "string",
      "enum": ["empty_frame", "screen_distraction", "subject_too_small"]
    }
  }
}
```

## Prompt Rules

- State that the model must output only one JSON object, no markdown and no explanation.
- If human decisions are already fixed, state that the model must not change them.
- For visual labeling, require all camera IDs to be present.
- Tell the model to list only actually present negative tags; use `[]` when none are visible.
- Avoid asking for long rationales. Use short notes for human audit only.

## Token And Latency Diagnostics

First compare visible output with usage:

- `raw_text_chars` should roughly match a small structured JSON response.
- `usage.output_tokens_details.reasoning_tokens` should be `0` when reasoning effort is `none`.
- Responses output should not contain an `output` item with `type: "reasoning"` or `encrypted_content`.

If `output_tokens` is thousands while visible JSON is small:

1. Check EAPIL gateway settings for forced reasoning overrides such as XHIGH.
2. Re-test with `/v1/responses`, strict schema, and `reasoning: {"effort":"none"}`.
3. Inspect whether `encrypted_content` appears in the raw response.
4. Do not blame the prompt until gateway forced reasoning is ruled out.

Known good reference from this project after gateway fix:

```text
endpoint: /v1/responses
model: gpt-5.5
reasoning: none
input_tokens: 1704
output_tokens: 230
reasoning_tokens: 0
visible JSON: about 786 chars
elapsed: about 8s
raw response: no encrypted_content
```

## Operational Rules

- Start teacher-labeling concurrency at 1-3. Raise only after observing stable latency and no 429/5xx bursts.
- Persist job status so interrupted labeling can resume from pending/error jobs.
- Log request mode, reasoning effort, usage, elapsed seconds, visible JSON length, parse status, and whether hidden reasoning exists.
- Keep real API keys out of logs and saved request artifacts.

## Smoke Test Script

Use `scripts/smoke_structured_output.py` to verify gateway behavior before large labeling runs:

```bash
python /root/.codex/skills/eapil-llmapi-structured/scripts/smoke_structured_output.py \
  --base-url https://api-n-cd.playones.com/v1 \
  --model gpt-5.5 \
  --endpoint responses \
  --reasoning-effort none \
  --api-key-env OPENAI_API_KEY
```

Add `--image /path/to/frame.jpg` one or more times for vision tests. Add `--dry-run` to print the request shape without calling the API.
