# แนะนำ OctoHub: ประตูหน้าเดียวสำหรับทุก LLM ที่เอเจนต์ของคุณเรียกใช้

> OctoHub คือ LLM proxy แบบ self-hosted ที่คุณวางไว้หน้าเอเจนต์ของคุณ มีเอนด์พอยต์เดียว มี alias ของโมเดล มีการกระจายโหลดต่อผู้ให้บริการ และมีบันทึกทุกคำขอที่ส่งออกไปพร้อมต้นทุนของมัน โอเพนซอร์ส เขียนด้วย Rust ภายใต้ Apache-2.0

# แนะนำ OctoHub: ประตูหน้าเดียวสำหรับทุก LLM ที่เอเจนต์ของคุณเรียกใช้

เอเจนต์ของคุณเพิ่งรันงานเสร็จ มันเรียกโมเดลไปสี่สิบครั้ง บางครั้งไปที่เครื่อง Ollama ในเครื่อง บางครั้งไป Anthropic และอีกครั้งไป OpenAI เพราะโมเดลในเครื่องสำลักกับคอนเท็กซ์ยาว ๆ งานเสร็จแล้ว ทีนี้ตอบผมสามคำถาม: มันส่งอะไรออกไปกันแน่ ราคาเท่าไร และอัปสตรีมไหนที่ตอบจริง ๆ

ถ้าคุณเป็นเหมือนเราในตอนนั้น คำตอบที่ซื่อสัตย์คือ "ผมคงต้องไปใส่ print แล้วรันใหม่อีกรอบ" แดชบอร์ดของผู้ให้บริการแสดงเฉพาะส่วนของมัน ล็อกของเอเจนต์แสดงเฉพาะส่วนของมัน ไม่มีใครแสดงคำขอทั้งก้อนตามที่มันวิ่งผ่านสายไป พร้อมแท็กว่าคีย์ไหนออกมันและผู้ให้บริการไหนรับไป มุมมองที่มีประโยชน์ที่สุด — ทุกคำขอผ่านหน้าจอเดียว — กลับเป็นสิ่งที่ไม่มีใครให้ เพราะไม่มีจุดเดียวที่คำขอทั้งหมดวิ่งผ่าน

**OctoHub คือจุดเดียวนั้น** มันคือ LLM proxy แบบ self-hosted ที่คุณวางไว้หน้าเอเจนต์ของคุณ พวกมันคุยกับเอนด์พอยต์เดียว OctoHub คุยกับใครก็ตามที่คุณบอก และจดทุกอย่างที่เกิดขึ้นระหว่างนั้นไว้ วันนี้เราเปิดซอร์สมัน

---

## มันคืออะไร ในแผนภาพเดียว

```
            ┌──────────────────┐
 agent   →  │  OctoHub proxy   │  →  openai
            │  (Rust / hyper)  │  →  anthropic
            │                  │  →  ollama (your GPU)
            └──────────────────┘  →  openrouter
                     │
                     └─→ your DB (SQLite / MySQL / PostgreSQL)
                         (api_keys, completions, embeddings)
```

ไบนารี Rust ตัวเดียวที่สร้างบน hyper ไคลเอนต์ยิงเข้าเอนด์พอยต์ HTTP เดียว พรอกซีแปลงชื่อโมเดล เลือกอัปสตรีม ส่งต่อคำเรียกผ่าน [octolib](/blog/octolib-the-engine-behind-our-ai-stack) — ไลบรารีไคลเอนต์ LLM ของเรา ตัวเดียวกับที่ทุกเครื่องมือของ Muvon ใช้คุยกับโมเดล — แล้วบันทึกคำขอ คำตอบ จำนวนโทเคน และเวลาหน่วงลงไป ตัวพรอกซีเองไม่มีสถานะระหว่างคำขอ สถานะอยู่สองที่: `octohub.toml` สำหรับการตั้งค่า และฐานข้อมูลสำหรับคีย์ ล็อก และการใช้งาน

มันจงใจ _ไม่ใช่_ หลายอย่าง มันไม่ใช่ UI แชต — ให้เล็งแอปพลิเคชันของคุณมาที่มัน มันไม่ใช่ตัวเราต์เชิงความหมายที่เลือกโมเดล "ดีที่สุด" สำหรับพรอมป์ต; การเลือกโมเดลเป็นหน้าที่ของผู้เรียก มันไม่ใช่ vector store; embedding วิ่งผ่านและถูกบันทึก แต่ OctoHub ไม่ทำดัชนีให้ มันเพิ่มการยืนยันตัวตน การบันทึก การกระจายโหลด และอินเทอร์เฟซที่เสถียร มันไม่พยายามฉลาดกับสิ่งที่ผู้ให้บริการส่งกลับมา — ทุกฟิลด์ที่มีความหมายกลับไปหาไคลเอนต์ตามเดิมเป๊ะ

---

## ทำไมเราถึงสร้างมัน

เราไม่ได้ตั้งใจจะสร้างพรอกซี เรากำลังรัน [Octomind](https://octomind.run) รันไทม์เอเจนต์ของเรา บนโมเดลผสม ๆ กัน — มีฟลีตแบบ self-hosted บน GPU ของเราเองสำหรับงานปริมาณมากราคาถูก และ API ระดับแนวหน้าสำหรับงานยาก ตัวระบบทำงานได้ สิ่งที่ทำไม่ได้คือการ _มองเห็น_ มัน

ทันทีที่คุณมีโมเดลมากกว่าหนึ่งตัวอยู่หลังแอปพลิเคชันเดียว คุณก็เสียหน้าจอเดียวนั้นไป ผู้ให้บริการแต่ละรายมีแดชบอร์ดของตัวเอง มีการนับโทเคนของตัวเอง มีนิยามของ "คำขอ" ของตัวเอง เอเจนต์ของคุณก็มีล็อกของมันเอง ซึ่งบอกสิ่งที่มัน _คิดว่า_ มันส่งออกไป เวลารันหนึ่งครั้งแพงกว่าที่คาด หรือโมเดลเริ่มคืนขยะออกมา คุณก็ต้องเย็บเรื่องราวที่ไม่ครบสามชิ้นเข้าด้วยกัน แล้วเดาตรงรอยต่อ

เราเขียนถึงความเจ็บปวดเฉพาะจุดนี้ไว้มากกว่านี้ — การอยากเห็นว่าเอเจนต์ทำอะไรจริง ๆ ไม่ใช่สิ่งที่มันอ้าง — ใน [observability ที่เราต้องการ](/blog/debugging-ai-agents-the-observability-we-wanted) OctoHub คือคำตอบเชิงโครงสร้างพื้นฐานต่อมัน วางพรอกซีหนึ่งตัวไว้บนเส้นทาง แล้วหน้าจอเดียวก็เกิดขึ้นโดยการออกแบบ: มันมีจุดเดียวจริง ๆ ที่ทุกคำขอวิ่งผ่าน ดังนั้นจึงมีจุดเดียวที่จะบันทึกมันได้

เหตุผลที่สองคือการกระจายโหลด เราก็ [รันเอเจนต์เดียวข้ามหลายโมเดล](/blog/running-one-ai-agent-across-many-models) ด้วยมืออยู่แล้ว — สลับคอนฟิก ตั้งตัวแปรสภาพแวดล้อม เปลี่ยนชื่อโมเดลทุกครั้งที่รัน เราต้องการ _alias_ ของโมเดลที่หมายความว่า "อันไหนก็ได้ในอัปสตรีมพวกนี้ แล้วแต่เธอเลือก" และมีลิมิตการทำงานพร้อมกัน เพื่อไม่ให้คำเรียกของเอเจนต์ที่พรั่งพรูเข้ามาเผาเครื่อง GPU ในเครื่อง ทั้งสองอย่างควรอยู่ในพรอกซี ไม่ใช่กระจัดกระจายอยู่ในทุกไคลเอนต์

---

## คำขอไหลผ่านมันอย่างไร

ลองดู `POST /v1/completions` นี่คือสิ่งที่ OctoHub ทำกับมัน:

1. **ยืนยันตัวตน** โทเคน `Authorization: Bearer` คือคีย์ API ของไคลเอนต์จากตาราง `api_keys` OctoHub ค้นหามัน ตรวจว่ายังใช้งานได้ แล้วแท็กคำขอด้วย ID ของคีย์ (ถ้าคุณสตาร์ทเซิร์ฟเวอร์โดยไม่มี master key จะปิดเฉพาะ admin API เท่านั้น — มีคำเตือนพิมพ์ออกมาตอนสตาร์ท ส่วนการเรียก completion และ embedding ยังต้องใช้คีย์ไคลเอนต์ที่ใช้งานได้)
2. **ตรวจ allow-list** คีย์ไคลเอนต์จำกัดให้ใช้เฉพาะชุดโมเดลได้ คีย์ที่ถูกจำกัดไว้ที่ `["gpt", "anthropic:claude-haiku-4-5"]` แล้วขออย่างอื่นจะได้ `403`
3. **แปลงโมเดล** ถ้าฟิลด์ `model` เป็น alias จาก `[models]` OctoHub จะขยายมันเป็นสตริง `provider:model` ถ้ามันเป็น `provider:model` เปล่า ๆ อยู่แล้ว ก็ผ่านตรงไป alias ที่แมปไปยัง _รายการ_ จะเริ่มจากตำแหน่งสุ่มแล้วเลือกผู้ให้บริการแรกที่รับได้ — นั่นคือการกระจายโหลด
4. **ขอ permit ของผู้ให้บริการ** ถ้าคุณตั้งลิมิตการทำงานพร้อมกันของผู้ให้บริการนั้นไว้ คำขอจะรอช่องว่าง ไม่มีช่องก็ไม่มี `429` — การเชื่อมต่อ HTTP จะค้างเปิดไว้จนกว่าจะมีช่องว่าง — นานสุดตามไทม์เอาต์ของคิว (ค่าเริ่มต้น 60 วินาที) จากนั้นจะได้ `503` เป็นการคุมอัตราโดยตั้งใจ บันทึกเป็นเวลารอในคิว
5. **เรียกอัปสตรีม** ผ่าน octolib พร้อมเส้นตายของการดำเนินการที่ตั้งค่าได้
6. **บันทึกและตอบกลับ** คำขอทั้งก้อน คำตอบ จำนวนโทเคน ต้นทุน ผู้ให้บริการที่แปลงได้ และเวลาหน่วง ลงตาราง `completions` ไคลเอนต์ได้คำตอบของอัปสตรีมตามเดิม พร้อมเฮดเดอร์ `X-Request-Id`

เจ้า `X-Request-Id` นั่นคือคีย์สำหรับเชื่อมข้อมูล มันคือค่าที่คุณส่งเข้ามา (ตรวจสอบแล้ว ส่งกลับ) หรือ ULID ตัวใหม่ มันปรากฏเป็น `req_id` ในทุกบรรทัดล็อกของคำขอนั้น หยิบคำตอบที่เป็น error คว้าเฮดเดอร์ grep ในล็อกของคุณ — คุณก็ได้เรื่องราวทั้งหมด

---

## Alias ของโมเดลและการกระจายโหลด

ส่วน `[models]` คือที่ที่ชื่อเดียวแผ่ออกไปสู่อัปสตรีมหลายตัว:

```toml
[models]
# หนึ่ง alias หนึ่งอัปสตรีม
"sonnet"  = ["anthropic:claude-sonnet-5"]

# หนึ่ง alias หลายอัปสตรีม — OctoHub สุ่มเลือกหนึ่งตัวต่อคำขอ
"workhorse" = ["ollama:kimi-k2.6", "ollama:minimax-m3", "openrouter:google/gemini-3.1-pro-preview"]

[embedding_models]
"voyage" = ["voyage:voyage-4"]
```

ไคลเอนต์ที่ขอ `workhorse` จะได้หนึ่งในสามตัว — OctoHub เริ่มจากตำแหน่งสุ่มแล้วเลือกผู้ให้บริการแรกที่หน้าต่างอัตรารับคำขอได้ นั่นคือโมเดลการกระจายโหลดทั้งหมด — เรียบง่าย คาดเดาได้ และพอดีพอที่จะกระจายทราฟฟิกของเอเจนต์ข้ามฟลีตหรือข้ามคีย์ได้โดยไม่ต้องมีตัวเราต์แยก วันนี้ยังไม่มีการเราต์แบบถ่วงน้ำหนัก; ผู้ให้บริการที่อยู่ในช่วงคูลดาวน์จากข้อผิดพลาดจะถูกลดลำดับไปอยู่หลังผู้ให้บริการที่ปกติ และคำขอที่ต่อเชนจะยึดผู้ให้บริการที่รับเทิร์นก่อนหน้า เราอยากปล่อยเวอร์ชันซื่อสัตย์ของมันมากกว่าที่จะบอกใบ้ถึงตัวจัดคิวที่ฉลาดเกินกว่าที่มีอยู่จริง

ไคลเอนต์ยังข้าม alias ไปเลยก็ได้ และส่ง `provider:model` เปล่า ๆ อย่าง `openai:gpt-5.5` ตาราง alias เป็นความสะดวก ไม่ใช่ด่าน — ด่านคือ allow-list ต่อคีย์

---

## โมเดล `auto`: บอกว่าทำไม ไม่ใช่ว่าตัวไหน

ตั้งแต่เวอร์ชัน 0.6.0 มีวิธีเลือกโมเดลอีกแบบ และเป็นวิธีที่เอเจนต์ของเราเองใช้มากที่สุด แทนที่จะระบุชื่อโมเดล ไคลเอนต์ส่ง `"model": "auto"` พร้อมเฮดเดอร์ `X-Model-Purpose` — สตริงอะไรก็ได้ตามใจ — แล้ว OctoHub จะแปลงวัตถุประสงค์นั้นเป็น alias:

```toml
[auto]
default = "workhorse"
compression = "cheap"
supervisor = "sonnet"
```

วัตถุประสงค์เป็นแบบลำดับชั้น แยกด้วย `-`: `supervisor-gate` จะ fallback ไปที่ `supervisor` แล้วไปที่ `default` แถว `supervisor` เพียงแถวเดียวครอบคลุมทุกวัตถุประสงค์ `supervisor-*` จนกว่าคุณจะปักหมุดตัวเฉพาะ — คุณนิยามแถวเท่าที่คุณมีความเห็นเท่านั้น วัตถุประสงค์ที่หายไปหรือพิมพ์ผิดจะเดเกรดไปที่ `default` ไม่เคยล้มเหลว

ทำไมต้องทำ? เพราะผู้เรียกมักรู้ว่า _กำลังทำคำเรียกแบบไหน_ — รอบบีบอัด การตรวจเกต ลูปหลัก — ส่วนผู้ดูแลรู้ว่าคำเรียกแบบนั้นสมควรอยู่เทียร์ไหน การเราต์ตามวัตถุประสงค์ย้ายการตัดสินใจนั้นไปไว้ในคอนฟิกของพรอกซี (หรือในแผนที่ override ต่อเจ้าของผ่าน `PUT /v1/admin/owners/:owner/auto` ซึ่งชนะพื้นฐานจากคอนฟิกทั้งหมด) แทนที่จะฝังชื่อโมเดลไว้ในเอเจนต์ Octomind ส่ง `main`, `compression` และตระกูล `supervisor-*` มาให้ตั้งแต่แรก

---

## การทำงานพร้อมกันต่อผู้ให้บริการ

API ระดับแนวหน้าของคุณรับสามสิบสองคำขอพร้อมกันได้แบบไม่กระพริบตา เครื่อง GPU เดียวของคุณที่รัน Ollama รับไม่ไหว ดังนั้น OctoHub จึงจำกัดจำนวนคำขอที่กำลังวิ่งต่อผู้ให้บริการ:

```toml
[providers.ollama]
concurrency = 5

[providers.openai]
concurrency = 32
```

คำขอที่เกินลิมิตจะเข้าคิว _ภายในโปรเซสของ OctoHub_ — การเชื่อมต่อของไคลเอนต์ถูกบล็อกจนกว่าจะมีช่องว่าง ผู้ให้บริการที่คุณไม่ได้ระบุไว้จะรันโดยไม่จำกัด ตัวจำกัดเป็นแบบ process-local และนับ completions กับ embeddings รวมกัน เพราะทั้งคู่ไหลผ่านการเชื่อมต่ออัปสตรีมเดียวกัน มันคือ semaphore หนึ่งตัวต่อผู้ให้บริการ ไม่มีอะไรพิสดาร แต่หมายความว่าเอเจนต์ที่กระจายสี่สิบคำเรียกออกไปจะไม่ล้มเซิร์ฟเวอร์โมเดลที่คำเรียกพวกนั้นไปลง

การทำงานพร้อมกันไม่ใช่ปุ่มเดียว ผู้ให้บริการแต่ละรายยังรับลิมิตอัตราแบบหน้าต่างคงที่ — `requests_per_minute`, `tokens_per_minute`, `requests_per_day`, `tokens_per_day` — และ alias ที่มีหลายอัปสตรีมจะหมุนไปยังตัวเลือกถัดไปเมื่อหน้าต่างเต็ม นั่นคือ "หน้าต่างอัตรา" ที่ตัวเลือก alias ตรวจสอบ: ผู้ให้บริการรายหนึ่งใช้งบโทเท็นรายวันหมดไม่ได้ทำให้คำขอล้มเหลว แค่ย้ายทราฟฟิกไปยังอัปสตรีมถัดไปที่ยังมีที่ว่าง

---

## observability ที่คุณได้จริง ๆ

นี่คือส่วนที่เราสร้าง OctoHub ขึ้นมาเพื่อมัน มันจึงมีสองพื้นผิว

**ล็อกแบบมีโครงสร้าง** บน stdout — สวยถ้าคุณอยู่บน TTY ไม่งั้นเป็น JSON ทุกคำขอที่เสร็จจะปล่อยออกมาหนึ่งบรรทัด:

```json
{
	"level": "INFO",
	"message": "request completed",
	"req_id": "01HMQGSB3R",
	"route": "/v1/completions",
	"status": 200,
	"dur_ms": 1523,
	"api_key_id": 1,
	"model": "workhorse",
	"provider": "ollama",
	"queued_ms": 0,
	"tok_in": 56,
	"tok_out": 120
}
```

คุณเห็นคีย์ที่ออกมัน ชื่อโมเดลที่ไคลเอนต์ขอ ผู้ให้บริการที่ตอบจริง มันเข้าคิวนานเท่าไร ใช้เวลาเท่าไร และโทเคนเข้าและออก ฟิลด์ provider คือคำตอบของ "การสุ่มเลือกไปลงที่อัปสตรีมไหน" — โดยไม่ต้องรันอะไรซ้ำ

**เมตริก Prometheus** บนพอร์ตแยก (ค่าเริ่มต้น `127.0.0.1:9090`, `GET /metrics`) ทุกตัวมีคำนำหน้า `octohub_`:

| เมตริก                                | บอกอะไรคุณ                                    |
| ------------------------------------- | --------------------------------------------- |
| `octohub_requests_total`              | ปริมาณคำขอตาม route, method, status           |
| `octohub_request_duration_seconds`    | ฮิสโตแกรมเวลาหน่วงปลายถึงปลาย                 |
| `octohub_completions_total`           | ปริมาณ completion ตาม model, provider, status |
| `octohub_completion_tokens_total`     | โทเคนเข้า/ออกตาม model และ provider           |
| `octohub_provider_queue_wait_seconds` | เวลารอ permit การทำงานพร้อมกัน                |
| `octohub_provider_in_flight`          | คำขอที่กำลังทำงานในแต่ละ provider ตอนนี้      |

ฮิสโตแกรมเวลารอในคิวคือตัวบ่งชี้ล่วงหน้าของความอิ่มตัว — เมื่อ P99 ไต่ขึ้น ฟลีตของคุณกลายเป็นคอขวดก่อนที่คำขอใดจะหมดเวลา PromQL ไม่กี่บรรทัดให้อัตราข้อผิดพลาดตามโมเดลและโทเคนออกต่อวินาทีตามผู้ให้บริการ เปิด `per_key = true` แล้วเมตริก completion จะได้ป้าย `api_key_id` เพิ่ม เพื่อให้คุณคิดเงินหรือปันต้นทุนต่อไคลเอนต์ได้ (ระวังคาร์ดินาลิตีถ้าคุณออกคีย์เป็นพัน ๆ ใบ)

สำหรับบันทึกฉบับเต็ม — ไม่ใช่ค่ารวม แต่เป็นไบต์จริงของพรอมป์ตและคำตอบ — admin API เสิร์ฟประวัติ completions ดิบ ๆ ตรงจากฐานข้อมูล

---

## คีย์แบบหลายผู้เช่าและ admin API

OctoHub มีชั้นยืนยันตัวตนสองชั้น **master key** (ตั้งใน `octohub.toml`) ปกป้อง admin API **คีย์ไคลเอนต์** — ออกผ่าน admin API นั้น เก็บในฐานข้อมูล — ยืนยันตัวตนเอนด์พอยต์ completion และ embedding ทุก completion ถูกแท็กด้วยคีย์ที่ออกมัน ซึ่งเป็นสิ่งที่ทำให้การติดตามการใช้งานต่อผู้เช่าทำงานได้

มี shell wrapper ชื่อ `octohub-admin.sh` สำหรับงานประจำวัน:

```bash
export OCTOHUB_MASTER_KEY=your-master-secret

# ออกคีย์ไคลเอนต์ จำกัดที่สองโมเดล
./octohub-admin.sh keys create ci-pipeline --allowed-models gpt,anthropic:claude-haiku-4-5

# การใช้งานรายวันของคีย์ 1 และ 2
./octohub-admin.sh usage --bucket day --key 1,2

# ดึง completions ดิบ 20 รายการล่าสุด — input และ output ฉบับเต็ม
./octohub-admin.sh completions --limit 20
```

คีย์ถูกเพิกถอน ไม่เคยถูกลบ — บันทึกการใช้งานผูกกับ ID ของคีย์ ประวัติจึงอยู่รอด การใช้งานถูกรวมยอดตาม `hour`, `day`, `week` หรือ `month` กรองตามคีย์และช่วงเวลาได้ ข้อมูลเดียวกันใช้ผ่าน HTTP ธรรมดาได้ถ้าคุณไม่อยากใช้สคริปต์

ยังมีพื้นผิวด้านการดูแลระบบอีกสองอย่างที่ควรรู้ `GET /v1/admin/status` รายงานสุขภาพต่อโมเดลที่สังเกตจากทราฟฟิกจริง — ไม่ใช่โพรบจำลอง ดังนั้นสะดุดครั้งเดียวจะไม่วาดไฟแดงบนแดชบอร์ดของคุณ และการส่ง `SIGHUP` ให้โปรเซสจะรีโหลด `octohub.toml` ทันที — alias ใหม่ ลิมิตใหม่ โดยไม่ต้องรีสตาร์ท

---

## รันมันใน 5 นาที

OctoHub คือไบนารีตัวเดียว สร้างมัน เขียนคอนฟิก สตาร์ทมัน ออกคีย์

```bash
# 1. สร้างจากซอร์ส
git clone https://github.com/Muvon/octohub
cd octohub && cargo build --release
```

เขียน `octohub.toml` แบบน้อยที่สุด:

```toml
[server]
host = "127.0.0.1"
port = 8080
api_key = "your-master-secret"        # เปิด auth + admin API
db_url = "sqlite://octohub.db"         # สร้างสคีมาอัตโนมัติตอนรันครั้งแรก

[models]
"workhorse" = ["ollama:kimi-k2.6", "openrouter:google/gemini-3.1-pro-preview"]

[metrics]
enabled = true
bind = "127.0.0.1:9090"

[providers.ollama]
concurrency = 5
```

สตาร์ทมันแล้วสร้างคีย์ไคลเอนต์:

```bash
# 2. สตาร์ทเซิร์ฟเวอร์ (ชี้คอนฟิกด้วย -c ถ้าไม่ได้อยู่ใน cwd)
./target/release/octohub

# 3. ออกคีย์ไคลเอนต์
curl -X POST http://127.0.0.1:8080/v1/admin/keys \
  -H "Authorization: Bearer your-master-secret" \
  -H "Content-Type: application/json" \
  -d '{"name": "my-app"}'
# → {"id": 1, "key": "abc...xyz", ...}   ← เก็บไว้ มันแสดงแค่ครั้งเดียว

# 4. ทำ completion ผ่านพรอกซี
curl -X POST http://127.0.0.1:8080/v1/completions \
  -H "Authorization: Bearer abc...xyz" \
  -H "Content-Type: application/json" \
  -d '{"model": "workhorse", "input": "Explain Rust in one sentence."}'
```

ฐานข้อมูลเริ่มต้นเป็น SQLite — ไม่ต้องตั้งค่าอะไร ชี้ `db_url` ไปที่ MySQL หรือ PostgreSQL เมื่อคุณโตเกินมัน; สคีมาถูกสร้างอัตโนมัติตอนเชื่อมต่อครั้งแรก คีย์ API ของผู้ให้บริการ (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY` และพวกพ้อง) อยู่ในสภาพแวดล้อม อ่านโดย octolib ตรงตามที่ผู้ให้บริการคาดหวัง

OctoHub ยังพูด OpenAI Chat Completions แบบดั้งเดิมที่ `POST /v1/chat/completions` ด้วย ดังนั้น SDK หรือเครื่องมือใด ๆ ที่เข้ากันได้กับ OpenAI ก็ชี้มาที่มันเป็น base URL แบบสลับใช้แทนกันได้ทันที (ข้อแม้ที่ซื่อสัตย์: ยังไม่รองรับ streaming — คำขอที่มี `"stream": true` จะได้ `501`)

---

## ชี้ Octomind มาที่มัน

OctoHub และ [Octomind](https://octomind.run) ถูกสร้างมาเพื่อกันและกัน และ octolib ก็มาพร้อมผู้ให้บริการ `octohub:` แบบเนทีฟ — ไม่ใช่ shim เข้ากันได้กับ OpenAI แต่พูด Responses API ของ OctoHub โดยตรง ตัวแปรสภาพแวดล้อมสองตัวเชื่อมพวกมันเข้าด้วยกัน:

```bash
export OCTOHUB_API_URL=http://127.0.0.1:8080   # เซิร์ฟเวอร์ OctoHub ของคุณ
export OCTOHUB_API_KEY=abc...xyz               # คีย์ไคลเอนต์ที่คุณออก
```

ตอนนี้การอ้างอิงโมเดลใด ๆ ใน Octomind ในรูปแบบ `octohub:<alias>` จะเราต์ผ่านพรอกซี:

```bash
octomind run --model octohub:workhorse developer:general
```

เอเจนต์คิดว่ามันกำลังคุยกับผู้ให้บริการเดียว แต่หลังพรอกซี `workhorse` แผ่ออกไปทั่วฟลีตของคุณ ทุกคำเรียกถูกบันทึกพร้อมต้นทุนและอัปสตรีมที่ตอบ และลิมิตการทำงานพร้อมกันก็ช่วยให้เครื่อง GPU ยืนได้ เอเจนต์ยังเรียบง่าย ส่วนการมองเห็นอยู่ที่ ๆ คำขอวิ่งข้ามสายจริง ๆ

และนี่ไม่ใช่การทดลองในห้องแล็บ [Octomind Cloud](/blog/octomind-cloud-prelaunch) — รันไทม์เอเจนต์แบบมีผู้จัดการของเรา — รันเอเจนต์ของลูกค้าทุกตัวผ่าน OctoHub บนโปรดักชัน คำเรียกมาพร้อมแท็กวัตถุประสงค์ (`main`, `compression`, `supervisor-gate`) โมเดล `auto` จะเราต์แต่ละคำเรียกไปยังเทียร์ที่เหมาะ และทุกคำขอลงในบันทึกพร้อมต้นทุนและอัปสตรีมที่ตอบ ไบนารีตัวเดียวกับที่คุณโคลนได้คือตัวที่ยืนอยู่หน้าฟลีตของเรา

---

## โอเพนซอร์ส Rust Apache-2.0

OctoHub อยู่ [บน GitHub](https://github.com/Muvon/octohub) ภายใต้ Apache-2.0 มันคือไบนารี Rust ตัวเดียวที่สร้างบน hyper — เล็ก เร็ว และพึ่งพาน้อย ใช้ SQLite เป็นค่าเริ่มต้นจึงไม่มีอะไรต้องตั้ง มี MySQL และ PostgreSQL เมื่อคุณต้องการ การตั้งค่าคือไฟล์ TOML หนึ่งไฟล์บวกกับ override สภาพแวดล้อม `OCTOHUB_*` อีกหยิบมือสำหรับสิ่งที่คุณปรับต่อการดีพลอย (`OCTOHUB_DB_URL`, `OCTOHUB_LOG_FORMAT`, `OCTOHUB_METRICS_BIND`)

มันยังเป็นช่วงต้น — เวอร์ชัน 0.6.4 ตัวเลขที่ซื่อสัตย์ มันทำตามที่บอก: ประตูหน้าเดียว alias ของโมเดล การเราต์ `auto` ตามวัตถุประสงค์ การกระจายโหลดพร้อม failover และคูลดาวน์ การทำงานพร้อมกันและลิมิตอัตราต่อผู้ให้บริการ การตรวจ modality ที่ข้ามผู้ให้บริการที่รับรูปหรือวิดีโอของคุณไม่ไหว คีย์แบบหลายผู้เช่า การบันทึกคำขอฉบับเต็ม และเอนด์พอยต์ Prometheus การรองรับผู้ให้บริการยังโตเรื่อย ๆ — Google Studio มาในเวอร์ชัน 0.6.2 เพิ่มจากยี่สิบกว่าตัวที่มีอยู่ มันยังไม่ทำ streaming หรือการเราต์แบบถ่วงน้ำหนัก และผมอยากบอกคุณตรง ๆ ดีกว่าให้คุณไปรู้เอาตอนอยู่บนโปรดักชัน

ถ้าคุณรันโมเดลมากกว่าหนึ่งตัวหลังแอปพลิเคชันเดียว — และถ้าคุณกำลังสร้างเอเจนต์ คุณก็รัน — OctoHub ให้หน้าจอเดียวที่สแต็กส่วนที่เหลือแอบสันนิษฐานว่ามีอยู่ โคลนมัน ชี้เอเจนต์มาที่มัน แล้วดูรันหนึ่งครั้งวิ่งผ่านไปพร้อมต้นทุนแนบมาด้วย

— Don

_OctoHub เป็นโอเพนซอร์สภายใต้ Apache-2.0 พัฒนาโดย [Muvon Un Limited](https://muvon.io) รับมันได้ที่ [GitHub](https://github.com/Muvon/octohub) — ยินดีรับ issues และ pull requests_
