# Open API

> ปลายทางที่เข้ากันได้กับ OpenAI สำหรับตัวละครของคุณ

Source: https://reverie.im/th/docs/integrations/open-api
Language: th

Reverie เปิดปลายทาง Chat Completions ที่เข้ากันได้กับ OpenAI ชี้ไคลเอนต์ OpenAI ตัวไหนก็ได้มาที่นี่ ส่งคีย์ API ของ Reverie ไปด้วย แล้วใช้ฟิลด์ `model` ระบุ**ตัวละครหรือฉากสถานการณ์**แทนที่จะเป็นโมเดลภาษา บทสนทนา ความทรงจำ และบทสรุปทั้งหมด Reverie เก็บไว้ฝั่งของมันเอง ดังนั้นแต่ละคำขอจึงต้องการแค่ข้อความใหม่ของคุณ

บัญชีที่ลงชื่อเข้าใช้แล้วใช้ได้ทุกบัญชี ไม่มีระดับการสมัครสมาชิกและไม่มีรายชื่อรอคิว — คุณต้องมีคีย์ API เท่านั้น แค่นั้นเอง [สร้างสักอัน →](https://reverie.im/th/docs/integrations/api-keys)

## URL หลักและการยืนยันตัวตน \[#base-url-and-auth]

```
https://reverie.im/api/device/v1
Authorization: Bearer rk_...
```

**การตั้งค่า → เพิ่มเติม → คีย์ API** จะแสดง URL หลักที่ถูกต้องในการ์ด **API Endpoint** พร้อมปุ่มคัดลอก

> **นี่คือ API ฝั่งเซิร์ฟเวอร์** คำขอจากเบราว์เซอร์ที่มาจากต้นทางอื่นจะถูกปิดกั้น ดังนั้น fetch จากหน้าเว็บของคุณเองจะล้มเหลวไม่ว่าคีย์จะถูกหรือไม่ก็ตาม ให้เรียกจากแบ็กเอนด์ และอย่าเอาคีย์ไปไว้ในโค้ดฝั่งไคลเอนต์

## เริ่มใช้งานอย่างรวดเร็ว \[#quick-start]

**cURL**

```bash
curl --no-buffer https://reverie.im/api/device/v1/chat/completions \
  -H "Authorization: Bearer $REVERIE_API_KEY" \
  -H "Content-Type: application/json" \
  --data-binary '{
  "model": "Luna [abc12345]",
  "messages": [
    {
      "role": "user",
      "content": "Hi"
    }
  ],
  "stream": true,
  "temperature": 0.8
}'
```

**Python**

```python
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["REVERIE_API_KEY"],
    base_url="https://reverie.im/api/device/v1",
)

stream = client.chat.completions.create(
    model="Luna [abc12345]",   # an id from GET /models, a full character or scenario ID, or omit entirely
    messages=[{"role": "user", "content": "Hi"}],
    stream=True,
    temperature=0.8,
)

for chunk in stream:
    print(chunk.choices[0].delta.content or "", end="")
```

**TypeScript**

```ts
import OpenAI from "openai";

const client = new OpenAI({
	apiKey: process.env.REVERIE_API_KEY,
	baseURL: "https://reverie.im/api/device/v1",
});

const stream = await client.chat.completions.create({
	model: "Luna [abc12345]",
	messages: [{ role: "user", content: "Hi" }],
	stream: true,
	temperature: 0.8,
});

for await (const chunk of stream) {
	process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
```

**การตั้งค่า → เพิ่มเติม → คีย์ API** ยังมีตัวสร้างคำขอ ที่จะสร้างโค้ดทั้งสามชุดนี้จากคีย์จริงอันหนึ่งของคุณกับตัวละครจริง

## ฟิลด์ `model` ระบุตัวละคร \[#the-model-field-names-a-character]

> **ใส่แค่ชื่อตัวละครไม่ได้ผล** `model: "Luna"` ไม่ได้ไปค้นหาตัวละครชื่อ Luna มันจับคู่ไม่ติดอะไรเลย แล้วไหลไปที่ตัวละครเริ่มต้นของคีย์หรือแชทล่าสุดของคุณ คุณก็เลยได้คำตอบจากตัวละครอื่นโดยไม่มีข้อผิดพลาดใด ๆ ใช้รูปแบบ `ชื่อ [ไอดีสั้น]` ที่ `GET /models` คืนมาเสมอ

| ค่าของ `model`                               | ใครตอบ                                                     |
| -------------------------------------------- | ---------------------------------------------------------- |
| `Luna [abc12345]`                            | ตัวละครหรือฉากสถานการณ์ที่ ID ลงท้ายด้วย 8 ตัวอักษรนั้น    |
| ID เต็มของตัวละครหรือฉากสถานการณ์            | ตัวละครหรือฉากนั้น                                         |
| ID โมเดลของ Reverie เช่น `deepseek-v4-flash` | เลือก**โมเดลภาษา** ส่วนตัวละครมาจากคีย์หรือแชทล่าสุดของคุณ |
| ไม่ใส่                                       | ตัวละครที่ตรึงไว้กับคีย์ ถ้าไม่มีก็คือแชทล่าสุดของคุณ      |

รายละเอียดที่ตัดสินว่าไอดีสั้นจะถูกแปลผลได้หรือไม่

* `[...]` ต้องอยู่**ท้าย**สตริง
* ส่วนท้ายที่ยาวไม่เกิน 8 ตัวอักษรจะถูกจับคู่เฉพาะกับตัวละคร**ที่คุณมีแชทด้วยอยู่แล้ว** ไม่ว่าจะบนเว็บหรือผ่าน API ส่วนไอดีสั้นของตัวละครที่คุณไม่เคยเปิดเลยจะหลุดไปเงียบ ๆ
* การค้นหาฉากสถานการณ์จะทำงานเฉพาะเมื่อคีย์ถูกตรึงไว้กับตัวละครเท่านั้น ถ้าใช้คีย์ทั่วไป ไอดีฉากแบบสั้นจะแปลผลไม่ได้เลย
* ถ้ายาวเกิน 8 ตัวอักษร จะถูกถือว่าเป็นไอดีเต็ม

### ดูว่าคุณคุยกับอะไรได้บ้าง \[#listing-what-you-can-talk-to]

```bash
curl https://reverie.im/api/device/v1/models \
  -H "Authorization: Bearer $REVERIE_API_KEY"
```

คีย์แบบ**ทั่วไป**จะคืนตัวละครล่าสุดของคุณ ส่วนคีย์ที่**ตรึงไว้กับตัวละคร**จะคืนฉากสถานการณ์ของตัวละครนั้นแทน หรือคืนตัวละครเองถ้ามันไม่มีฉากเลย แต่ละรายการหน้าตาแบบนี้

```json
{
	"id": "Luna [abc12345]",
	"object": "model",
	"created": 1759200000,
	"owned_by": "character",
	"permission": [],
	"root": "<full character id>",
	"parent": null
}
```

คัดลอก `id` ไปใส่ใน `model` ได้เลย ข้อผิดพลาดจากปลายทางนี้ใช้รูปแบบข้อผิดพลาดแบบเรียบ ๆ ไม่ใช่กรอบของ OpenAI

## เนื้อคำขออ่านอะไรบ้าง \[#what-the-request-body-reads]

มีสี่ฟิลด์ที่ถูกอ่าน นอกนั้นรับไว้แล้วทิ้งเงียบ ๆ

| ฟิลด์         | ชนิด       | ค่าเริ่มต้น                              | หมายเหตุ                                              |
| ------------- | ---------- | ---------------------------------------- | ----------------------------------------------------- |
| `messages`    | อาร์เรย์   | จำเป็น                                   | ดูด้านล่าง — ใช้แค่บางส่วนเท่านั้น                    |
| `model`       | สตริง      | ไม่บังคับ                                | ตัวละคร ฉากสถานการณ์ หรือโมเดลภาษา ตามที่ว่าไว้ข้างบน |
| `stream`      | บูลีน      | **`true`**                               | ต่างจาก API ของ OpenAI ตรงที่นี่สตรีมเป็นค่าเริ่มต้น  |
| `temperature` | ตัวเลข 0–2 | ค่าที่ตั้งไว้ของตัวละคร ถ้าไม่มีก็ `0.8` | ค่านอกช่วงจะถูกปฏิเสธ                                 |

ถูกเพิกเฉยโดยไม่เตือน `max_tokens`, `top_p`, `n`, `stop`, `presence_penalty`, `frequency_penalty`, `logit_bias`, `seed`, `user`, `response_format`, `tools`, `tool_choice`, `logprobs`, `stream_options`.

`messages` ถูกจัดการอย่างไร

* **ข้อความ `system` จะถูกทิ้ง** บุคลิกและพฤติกรรมมาจากฟิลด์ของตัวละครเอง ไม่ใช่จากคำขอ [ฟิลด์ของพรอมต์ →](https://reverie.im/th/docs/characters/prompt-fields)
* **ไม่รองรับการมองภาพ** เนื้อหาจะเป็นสตริงหรืออาร์เรย์ของส่วนย่อยก็ได้ แต่อ่านเฉพาะส่วนที่เป็น `text` เท่านั้น ส่วนที่เป็นรูปภาพจะถูกเพิกเฉย
* มีเพียง**ข้อความ `user` ข้อความสุดท้าย**เท่านั้นที่จะถูกเพิ่มเข้าบทสนทนา ข้อความของผู้ใช้ก่อนหน้านั้นจะถูกเพิกเฉย
* ข้อความของผู้ช่วยถูกใช้เพื่อซิงก์ประวัติเท่านั้น (ดูด้านล่าง)
* ถ้าข้อความสุดท้ายของผู้ใช้ว่างเปล่าหรือมีแต่ช่องว่าง จะได้ `missing_user_message` กลับมา

## การตอบกลับ \[#responses]

การตอบกลับแบบไม่สตรีมคืออ็อบเจกต์ `chat.completion` ปกติ โดย `finish_reason` จะเป็น `stop` เสมอ — การเรียกเครื่องมือภายในไม่เคยถูกเปิดออกมา — และตัวนับใน `usage` เป็นศูนย์ทั้งหมด ดังนั้นให้วัดโทเค็นจากสถิติการใช้งานในหน้าคีย์ API แทนที่จะดูจากการตอบกลับ

การตอบกลับแบบสตรีมคือ `text/event-stream`

1. ก้อนเนื้อหา คืออ็อบเจกต์ `chat.completion.chunk` ที่มี `delta.content` \*&#x2A;จะไม่มีการส่งก้อนแรกแบบ `delta: {"role": "assistant"}`*\*
2. ก้อนปิดท้าย ที่มี `finish_reason` เป็น `stop`, `length` หรือ `content_filter` ส่วนการจบเพราะเรียกเครื่องมือจะรายงานเป็น `stop`
3. ก้อนการใช้งาน ที่มีอาร์เรย์ `choices` ว่างเปล่า
4. `data: [DONE]`.

ถ้าการสร้างล้มเหลวกลางสตรีม สตรีมจะส่ง `data: {"error": {...}}` ออกมาแล้วตามด้วย `[DONE]`

## สถานะของบทสนทนา \[#conversation-state]

Reverie เป็นฝ่ายเก็บบทสนทนาไว้เอง ทุกคู่ของคีย์กับตัวละครจะมีแชทหนึ่งรายการ แชทนั้นจะโผล่ในเว็บแอปเคียงกับแชทอื่น ๆ ของคุณ และแต่ละตาจะประกอบขึ้นจากพรอมต์ระบบและฉากสถานการณ์ของตัวละคร ข้อกำหนดจากสมุดโลก ความทรงจำระยะยาว บทสรุปที่ทยอยสะสม สไตล์การเล่าเรื่องของตัวละคร ความยาวคำตอบและการตั้งค่า NSFW การล็อกภาษาจากภาษาของบัญชีคุณ และประวัติล่าสุดเท่าที่หน้าต่างบริบทของโมเดลจะรับไหว ส่วนเครื่องมือด้านความทรงจำจะทำงานเมื่อโมเดลรองรับเครื่องมือ

เพราะเซิร์ฟเวอร์เก็บประวัติไว้ให้แล้ว คุณจึงไม่ต้องส่งมันซ้ำ

> **การส่งข้อความของผู้ช่วยอาจลบประวัติได้** ถ้าอาร์เรย์ `messages` ของคุณมีข้อความของผู้ช่วยอยู่ Reverie จะหยิบข้อความสุดท้ายมาไล่หาในบรรดา 20 ข้อความล่าสุดที่เก็บไว้ และถ้าเจอที่ตรงกัน มันจะลบทุกข้อความที่เกิดขึ้นหลังจากนั้นอย่างถาวร พร้อมกับความทรงจำที่เกิดหลังจุดนั้นด้วย ถ้าไม่เจออะไรเลยก็จะไม่มีอะไรถูกลบ ความยาวของอาร์เรย์คุณไม่เกี่ยวข้อง ถ้าอยากเลี่ยงเรื่องนี้ให้หมดจด ก็ส่งเฉพาะข้อความใหม่ของผู้ใช้เท่านั้น

## โมเดลภาษาตัวไหนเป็นคนตอบ \[#which-llm-answers]

ตัวละครเป็นคนกำหนด ไม่ใช่คำขอ — ยกเว้นว่าคุณระบุโมเดลภาษาเอง ลำดับความสำคัญจากต่ำไปสูงคือ โมเดลแชทเริ่มต้น แล้วค่าโมเดลที่ตั้งไว้ของตัวละครเอง แล้ว ID โมเดลของ Reverie ที่ส่งมาใน `model` และสุดท้ายคือความชอบด้านโมเดลที่คุณตั้งไว้สำหรับแชทนั้นในเว็บแอป ซึ่งชนะทุกอย่างข้างต้น [โมเดล →](https://reverie.im/th/docs/reference/models)

## ข้อผิดพลาด \[#errors]

ข้อผิดพลาดจาก `/chat/completions` ใช้กรอบของ OpenAI `{"error": {"message", "type", "param": null, "code"}}`.

| HTTP        | `code`                 | `type`                  | สาเหตุ                                                                  |
| ----------- | ---------------------- | ----------------------- | ----------------------------------------------------------------------- |
| 401         | `invalid_api_key`      | `invalid_request_error` | `Authorization` ขาดหายหรือรูปแบบผิด คีย์ไม่รู้จัก หรือคีย์ถูกปิดใช้งาน  |
| 401         | `api_key_expired`      | `invalid_request_error` | คีย์เลยวันหมดอายุแล้ว                                                   |
| 404         | `scenario_not_found`   | `invalid_request_error` | แปลผล ID ฉากสถานการณ์ได้ แต่ฉากนั้นหายไปแล้ว                            |
| 404         | `character_not_found`  | `invalid_request_error` | ไม่มีตัวละครนี้หรือถูกลบไปแล้ว                                          |
| 404         | `no_chat_history`      | `invalid_request_error` | ไม่มี `model` ไม่มีตัวละครที่ตรึงไว้ และไม่มีแชทเก่าให้ถอยไปใช้         |
| 400         | `missing_user_message` | `invalid_request_error` | ไม่มีข้อความ `user` ที่ไม่ว่างเปล่า                                     |
| 429         | `rate_limit_exceeded`  | `rate_limit_exceeded`   | โควตาโมเดลฟรีในช่วงเวลานี้หมดแล้ว `Retry-After` จะบอกว่าต้องรอกี่วินาที |
| 429         | `insufficient_quota`   | `insufficient_quota`    | เครดิตไม่พอสำหรับคำขอนี้                                                |
| 502         | `MODEL_EMPTY_RESPONSE` | `server_error`          | โมเดลจบการทำงานเรียบร้อย แต่ไม่มีข้อความออกมา                           |
| แล้วแต่กรณี | โค้ดของผู้ให้บริการ    | `server_error`          | การสร้างล้มเหลวที่ต้นทาง                                                |

มีสองกรณีที่อยู่นอกกรอบนี้ คือข้อผิดพลาดของ `GET /models` และเนื้อคำขอที่ถูกการตรวจสอบปฏิเสธ (เช่น `temperature` นอกช่วง) — ทั้งสองกรณีจะคืนรูปแบบข้อผิดพลาดแบบเรียบ ๆ

## ขีดจำกัดอัตราและโควตา \[#rate-limits-and-quotas]

API นี้ **ไม่มีขีดจำกัดอัตราต่อคีย์ ไม่มีเพดานการเรียกพร้อมกัน และไม่มีโควตาคำขอ** มีเพียงสองอย่างที่จำกัดการใช้งานของคุณ

| ขอบเขต            | ค่า                                                                              |
| ----------------- | -------------------------------------------------------------------------------- |
| ยอดเครดิตคงเหลือ  | ตรวจก่อนทุกคำขอ ถ้าไม่พอจะคืน `insufficient_quota`                               |
| ช่วงโควตาโมเดลฟรี | 15 ข้อความต่อ 3 ชั่วโมงต่อบัญชี — Pro ×3 (45), Premium ×4 (60), Ultimate ×5 (75) |

ช่วงโควตาโมเดลฟรีใช้ร่วมกับแชทบนเว็บ เรื่องราว นิยาย และบอททั้งหลาย — มันเป็นโควตาเดียวต่อบัญชี ไม่ใช่โควตาแยกของแต่ละช่องทาง [โมเดลฟรี →](https://reverie.im/th/docs/reference/free-models)

Reverie ไม่ส่ง webhook ออกไปข้างนอก จึงไม่มีอะไรให้สมัครรับเพื่อรู้ว่าการสร้างเสร็จแล้ว

## การนำเข้าตัวละคร \[#importing-characters]

ยังมีปลายทางที่สอง `POST /api/v1/characters/import` ซึ่งสร้างตัวละครจากการ์ดตัวละคร มันต้องใช้คีย์ที่มีสิทธิ์นำเข้า ซึ่งไม่ใช่ชนิดของคีย์ที่หน้าการตั้งค่าสร้างให้ [คีย์ API →](https://reverie.im/th/docs/integrations/api-keys) และ [นำเข้าตัวละคร →](https://reverie.im/th/docs/characters/import-characters)

## ที่เกี่ยวข้อง \[#related]

* [คีย์ API](https://reverie.im/th/docs/integrations/api-keys) — สร้าง ตรึง กำหนดวันหมดอายุ และดูการใช้งานของคีย์
* [โมเดล](https://reverie.im/th/docs/reference/models) — ID ของโมเดลและตัวคูณเครดิต
* [ขีดจำกัด](https://reverie.im/th/docs/reference/limits) — เพดานอื่น ๆ ในผลิตภัณฑ์
* [เครดิต](https://reverie.im/th/docs/billing/credits) — คำขอถูกคิดราคาอย่างไร
