333 lines
18 KiB
Markdown
333 lines
18 KiB
Markdown
# Edge Local Quickstart
|
|
|
|
이 문서는 `toki-labs.com` Control Plane에 native Edge를 붙이고, Ollama Node를 연결해 OpenAI-compatible smoke까지 확인하는 최소 절차다.
|
|
|
|
설정 기준은 `edge.yaml`이다. 주소는 환경 변수로 흩뿌리지 않는다.
|
|
|
|
## 1. Package
|
|
|
|
현재 `toki-labs.com` 원격처럼 Apple Silicon macOS에서 실행한다.
|
|
|
|
```bash
|
|
make build
|
|
```
|
|
|
|
## 2. Edge 설정
|
|
|
|
```bash
|
|
rm -rf "$HOME/iop-edge-test"
|
|
mkdir -p "$HOME/iop-edge-test"
|
|
tar -xzf build/packages/iop-edge-darwin-arm64.tar.gz -C "$HOME/iop-edge-test"
|
|
cd "$HOME/iop-edge-test/iop-edge-darwin-arm64"
|
|
./iop-edge config init
|
|
```
|
|
|
|
`edge.yaml`에서 아래 값만 맞춘다. 모델만 필요하면 바꾼다.
|
|
|
|
### Provider-first 구성 (권장)
|
|
|
|
Provider-first는 `models[]`로 라우팅 키를 정의하고 `nodes[].providers[]`로 provider 후보를 선언한다. 두 구조를 `models[].providers[provider_id]` 키로 연결한다.
|
|
|
|
```yaml
|
|
models:
|
|
# Top-level catalog: canonical routing key → provider-pool mapping.
|
|
# models[].id is the external model id; providers maps provider id → served model.
|
|
- id: "gemma4:26b"
|
|
providers:
|
|
ollama-gemma: "gemma4:26b"
|
|
|
|
edge:
|
|
id: "edge-toki-labs"
|
|
name: "Toki Labs Edge"
|
|
|
|
server:
|
|
listen: "0.0.0.0:19090"
|
|
advertise_host: "toki-labs.com"
|
|
|
|
bootstrap:
|
|
listen: "0.0.0.0:18080"
|
|
artifact_base_url: "http://toki-labs.com:18080"
|
|
artifact_dir: "artifacts"
|
|
|
|
control_plane:
|
|
enabled: true
|
|
wire_addr: "toki-labs.com:19081"
|
|
|
|
refresh:
|
|
enabled: true
|
|
listen: "127.0.0.1:19093"
|
|
|
|
# Legacy adapter/target fallback (backward compat only).
|
|
# New deploys should use models[] + nodes[].providers[].
|
|
openai:
|
|
enabled: true
|
|
listen: "0.0.0.0:18081"
|
|
adapter: "ollama"
|
|
target: "gemma4:26b"
|
|
|
|
nodes:
|
|
- id: "node-ollama-1"
|
|
agent_kind: "generic-node"
|
|
providers:
|
|
- id: "ollama-gemma"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
base_url: "http://127.0.0.1:11434"
|
|
models:
|
|
- "gemma4:26b"
|
|
capacity: 2
|
|
```
|
|
|
|
`models[]`를 사용할 경우 `openai.adapter`/`openai.target`은 하향 호환 fallback이다. 실제 routing은 `nodes[].providers[].id`를 기준으로 provider-pool에서 선택된다.
|
|
|
|
확인:
|
|
|
|
```bash
|
|
./iop-edge --config edge.yaml env
|
|
./iop-edge --config edge.yaml config check
|
|
```
|
|
|
|
## 3. Node 등록
|
|
|
|
```bash
|
|
./iop-edge --config edge.yaml node register node-ollama-1 \
|
|
--adapter ollama \
|
|
--ollama-base-url http://127.0.0.1:11434
|
|
```
|
|
|
|
출력된 OS별 bootstrap 명령 원문을 보관한다. 명령 원문에는 실제 token이 포함되므로 tracked 문서에는 기록하지 않는다.
|
|
|
|
## 4. Edge 실행
|
|
|
|
```bash
|
|
mkdir -p logs run
|
|
nohup ./iop-edge --config edge.yaml serve > logs/edge.stdout.log 2>&1 &
|
|
echo $! > run/iop-edge.pid
|
|
```
|
|
|
|
확인:
|
|
|
|
```bash
|
|
curl -fsS http://toki-labs.com:18000/edges
|
|
```
|
|
|
|
## 5. Node 실행
|
|
|
|
3단계에서 출력된 bootstrap 명령을 Node host에서 그대로 실행한다. Linux/macOS는 generated `curl | bash` 명령을 사용하고, Windows native PowerShell은 generated `.ps1` bootstrap과 `Start-IopNode` 함수를 사용한다.
|
|
|
|
Node 연결 확인:
|
|
|
|
```bash
|
|
curl -fsS http://toki-labs.com:18000/edges/edge-toki-labs/status
|
|
```
|
|
|
|
## 6. dev-runtime provider pool 반복 기준
|
|
|
|
현재 GX10 vLLM, OneXPlayer Lemonade, Mac MLX vLLM을 같은 model alias로 묶어 검증하는 dev-runtime 기준은 원격 runner의 동기화된 iop checkout과 `build/dev-runtime/edge.yaml`이다.
|
|
|
|
배포 전 원격 runner checkout은 보존 대상이 아니다. 항상 clean sync 후 dev-runtime binary를 다시 빌드한다.
|
|
|
|
```bash
|
|
cd /Users/toki/agent-work/iop-dev
|
|
git fetch origin main
|
|
git reset --hard origin/main
|
|
git clean -fd
|
|
|
|
go build -trimpath -o build/dev-runtime/bin/edge ./apps/edge/cmd/edge
|
|
go build -trimpath -o build/dev-runtime/bin/iop-node ./apps/node/cmd/node
|
|
GOOS=linux GOARCH=arm64 go build -trimpath -o build/dev-runtime/bin/iop-node-linux-arm64 ./apps/node/cmd/node
|
|
GOOS=windows GOARCH=amd64 go build -trimpath -o build/dev-runtime/bin/iop-node-windows-amd64.exe ./apps/node/cmd/node
|
|
```
|
|
|
|
기준 주소:
|
|
|
|
- Control Plane status: 원격 host의 `http://127.0.0.1:18001`
|
|
- dev-runtime Edge id: `edge-toki-labs-dev`
|
|
- bootstrap HTTP: `http://toki-labs.com:18082`
|
|
- Edge OpenAI-compatible base URL: `http://toki-labs.com:18083/v1`
|
|
- Edge-Node TCP transport: `toki-labs.com:18084`
|
|
|
|
외부로 노출되는 OpenAI-compatible API는 `openai.bearer_token`으로 보호한다. tracked 문서에는 실제 token 원문을 기록하지 않고, Cline 같은 외부 client에는 운영자가 전달한 API key를 `Authorization: Bearer ...` 형태로 설정한다.
|
|
|
|
기준 provider pool:
|
|
|
|
- model alias: `qwen3.6:35b`
|
|
- provider candidate: `gx10-vllm` on `gx10-vllm-node`, capacity `4`, priority `0`
|
|
- provider candidate: `onexplayer-lemonade` on `onexplayer-lemonade-node`, capacity `3`, priority `1`
|
|
- provider candidate: `mac-mlx-vllm` on `mac-codex-node`, capacity `2`, priority `2`
|
|
|
|
Provider-pool dispatch는 capacity gate를 먼저 적용해 `in_flight >= capacity`인 provider를 후보에서 제외한다. 남은 후보 중 가장 낮은 `in_flight` 레벨을 우선 선택하며, 같은 `in_flight` 레벨 안에서 priority 숫자가 낮은 provider를 먼저 선택한다. 이 기준에서 10개 동시 요청은 정상적으로 `gx10-vllm=4`, `onexplayer-lemonade=3`, `mac-mlx-vllm=2`, queue `1` 구성이 된다.
|
|
|
|
GX10 vLLM은 `nvidia/Qwen3.6-35B-A3B-NVFP4`를 Docker container `iop-vllm-qwen36-prev-20260704063128`로 띄운다. dev 기준 endpoint는 `http://192.168.0.91:8001/v1`, runtime baseline은 `--max-model-len 262144`, `--max-num-seqs 4`, `--gpu-memory-utilization 0.30`, `--reasoning-parser qwen3`, `--default-chat-template-kwargs '{"enable_thinking":true}'`이다.
|
|
|
|
OneXPlayer Lemonade Node는 원격 runner나 Edge host에서 다시 SSH하거나 proxy process로 띄우지 않는다. 현재 작업 호스트에서 Windows host에 `ssh r0bin@192.168.0.59`로 직접 접속한 뒤, 그 host 안에서 generated PowerShell bootstrap 명령을 실행한다. 이때 Node 작업 경로는 `$HOME\iop-field`이며, Lemonade provider endpoint는 Windows host 로컬에서 접근 가능한 값을 기준으로 검증한다.
|
|
|
|
OneXPlayer Lemonade는 `Qwen3.6-35B-A3B-MTP-GGUF` artifact를 사용하되 runtime MTP speculative decoding은 끈다. dev long-context 기준 load 설정은 Vulkan backend, `ctx_size=524288`, `llamacpp_args="--spec-type none -np 3 -cb -fa on -b 4096 -ub 1024"`로 고정한다. llama.cpp server 기준 `ctx_size`는 전체 KV/context 예산이고 `-np`는 server slot 수이므로, 이 설정은 총 context 예산을 full `262144` window 2개분으로 제한하고 slot 3개를 둔다. 2026-07-04 관측 기준 `/slots`는 slot별 `n_ctx=174848`을 반환한다. 재시작 뒤에도 유지되도록 `/v1/load`는 `save_options=true`로 적용한다.
|
|
|
|
```bash
|
|
curl -fsS http://192.168.0.59:13305/v1/load \
|
|
-H 'Content-Type: application/json' \
|
|
-d '{"model_name":"Qwen3.6-35B-A3B-MTP-GGUF","ctx_size":524288,"llamacpp_backend":"vulkan","llamacpp_args":"--spec-type none -np 3 -cb -fa on -b 4096 -ub 1024","save_options":true}'
|
|
```
|
|
|
|
Mac MLX provider는 Edge host 자신에서 `vllm-mlx`로 띄운다. dev 기준 endpoint는 `http://127.0.0.1:8002/v1`, served model은 `mlx-community/Qwen3.6-35B-A3B-4bit`, provider capacity는 `2`이다. 작업 경로는 `/Users/toki/agent-work/iop-mlx-vllm`, Python 환경은 `.venv`의 Homebrew Python 3.12, 모델 cache는 `hf-cache`를 사용한다. 확인된 package baseline은 `vllm-mlx 0.3.0`, `mlx 0.31.2`, `mlx-lm 0.31.3`이다.
|
|
|
|
vllm-mlx process는 `--max-num-seqs 2`, `--max-kv-size 262144`, `--max-request-tokens 262144`, `--use-paged-cache --paged-cache-block-size 64 --max-cache-blocks 4096`로 고정한다. 이 설정은 병렬 슬롯을 `2`로 제한하고, KV/context 예산을 full `262144` window 1개분으로 맞춘다.
|
|
|
|
```bash
|
|
cd /Users/toki/agent-work/iop-mlx-vllm
|
|
export HF_HOME="$PWD/hf-cache"
|
|
export IOP_MLX_API_KEY="<edge.yaml local bearer token>"
|
|
nohup .venv/bin/vllm-mlx serve mlx-community/Qwen3.6-35B-A3B-4bit \
|
|
--offline \
|
|
--host 127.0.0.1 \
|
|
--port 8002 \
|
|
--api-key "$IOP_MLX_API_KEY" \
|
|
--max-num-seqs 2 \
|
|
--max-kv-size 262144 \
|
|
--max-request-tokens 262144 \
|
|
--use-paged-cache \
|
|
--paged-cache-block-size 64 \
|
|
--max-cache-blocks 4096 \
|
|
--enable-auto-tool-choice \
|
|
--tool-call-parser qwen \
|
|
--reasoning-parser qwen3 \
|
|
--default-chat-template-kwargs '{"enable_thinking": true}' \
|
|
--mllm \
|
|
> logs/vllm-mlx.stdout.log 2> logs/vllm-mlx.stderr.log &
|
|
echo $! > vllm-mlx.pid
|
|
```
|
|
|
|
Mac MLX provider의 주요 운영 파일은 `vllm-mlx.pid`, `logs/vllm-mlx.stdout.log`, `logs/vllm-mlx.stderr.log`다. 2026-06-24 직접 호출 측정 기준으로 `max_tokens=192`, `enable_thinking=false`에서 1/2/3 동시 총합은 약 `59.66`, `87.31`, `98.13 tok/s`였다.
|
|
|
|
SSH 세션 안의 `Start-Process`는 세션 종료와 함께 `iop-node.exe`가 정리될 수 있다. 반복 배포에서는 Windows host에서 `Win32_Process.Create` 방식으로 세션 독립 실행한다.
|
|
|
|
```powershell
|
|
$work = "$HOME\iop-field"
|
|
$cmd = 'cmd.exe /c "cd /d C:\Users\r0bin\iop-field && iop-node.exe --config node.yaml serve >> iop-node.wmi.stdout.log 2>> iop-node.wmi.stderr.log"'
|
|
Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{ CommandLine = $cmd; CurrentDirectory = $work }
|
|
```
|
|
|
|
capacity, provider mapping, model alias 변경은 후보 `edge.yaml` 수정 후 `config check`, refresh dry-run, refresh apply 순서로 반영한다. `config refresh` subcommand나 `19093` admin port가 없으면 dev-runtime binary rebuild 누락으로 보고 clean sync/rebuild부터 다시 수행한다.
|
|
|
|
dev-runtime 번들은 바이너리를 `build/dev-runtime/bin/edge`에 둔다. 명령은 그 경로를 `EDGE_BIN`으로 잡고 실행한다.
|
|
|
|
```bash
|
|
EDGE_BIN=build/dev-runtime/bin/edge
|
|
"$EDGE_BIN" --config build/dev-runtime/edge.yaml config check
|
|
"$EDGE_BIN" --config build/dev-runtime/edge.yaml config refresh --mode dry-run --addr 127.0.0.1:19093
|
|
"$EDGE_BIN" --config build/dev-runtime/edge.yaml config refresh --mode apply --addr 127.0.0.1:19093
|
|
```
|
|
|
|
Node host OS 재부팅은 필요하지 않다. Edge process 재시작이나 일시 단절은 Node reconnect 정책으로 처리한다. Node는 최초 연결 성공 이후 기본 `10s` 간격으로 최대 `10`회 재접속을 시도하므로, retry window 안에서 Edge가 회복되면 bootstrap 명령을 다시 실행하지 않는다. retry 한계를 넘겨 Node process가 종료된 경우에만 해당 host에서 새 bootstrap 실행이 필요하다.
|
|
|
|
반복 검증 순서:
|
|
|
|
1. Control Plane status에서 `edge-toki-labs-dev`의 connected node 수와 node id를 확인한다.
|
|
2. Edge host에서 `toki-labs.com:18084`의 established node TCP connection 수를 확인한다.
|
|
3. Edge process를 재시작하고 bootstrap 재실행 없이 3개 Node 연결이 회복되는지 확인한다.
|
|
4. `http://toki-labs.com:18083/healthz`와 `/v1/models`를 확인한다.
|
|
5. capacity를 `gx10-vllm=4`, `onexplayer-lemonade=3`, `mac-mlx-vllm=2`로 맞춘 후보 config를 refresh apply하고 Edge process가 유지되는지 확인한다. Node adapter capacity 변경이 `restart_required`로 반환되면 Edge process를 새 config로 재시작한 뒤 Node reconnect 상태를 확인한다.
|
|
6. `qwen3.6:35b`로 `/v1/responses`와 `/v1/chat/completions` 각각에 provider capacity 총합 + 1개 동시 요청을 보낸다.
|
|
7. capacity `gx10-vllm=4`, `onexplayer-lemonade=3`, `mac-mlx-vllm=2` 기준이면 endpoint별 9개 동시 요청에서 Control Plane status의 provider snapshot이 총 `in_flight=9`, `queued=0`으로 처리되어야 한다. 10개 동시 요청에서는 `in_flight=9`, `queued>=1`, 13개 동시 확장 smoke에서는 `in_flight=9`, `queued>=4`를 기대한다.
|
|
8. 요청 완료 후 provider snapshot이 `in_flight=0`, `queued=0`으로 회복되는지 확인한다.
|
|
|
|
2026-06-24 dev 13-way 확장 smoke는 Mac capacity가 `3`이던 이전 관측이다. 해당 run에서는 `/v1/chat/completions` 13개 요청이 모두 성공했고 peak `in_flight=10`, 실제 queue 대기 `3`개가 관측되었다. 현재 Mac capacity `2` 기준의 13-way smoke 기대치는 peak `in_flight=9`, queue 대기 `4`개 이상이다.
|
|
|
|
Qwen 계열 thinking 모델은 reasoning 텍스트를 포함해 응답할 수 있다. 이 dev smoke에서는 thinking 출력을 실패로 보지 않고, HTTP 성공, final marker 포함, provider log/run count 증가를 기준으로 판정한다.
|
|
|
|
### Request-level think-control smoke
|
|
|
|
`/v1/chat/completions` 요청은 `think`, `reasoning_effort`, `thinking_token_budget`, `include_reasoning`으로 thinking/reasoning 동작과 응답 노출을 제어할 수 있다. dev-runtime provider pool에서는 vLLM, vLLM-MLX, Lemonade 지원 범위가 다르므로 provider-isolated smoke로 provider별 기대 결과를 분리해 판정한다.
|
|
|
|
- 공통 안정 smoke: `think` 생략, `include_reasoning=false`, 또는 budget-only `thinking_token_budget`
|
|
- vLLM / Lemonade `think=false`: `chat_template_kwargs.enable_thinking=false`로 reasoning 생성을 끄고 content stream이 유지되어야 한다.
|
|
- vLLM-MLX `think=false` 또는 `reasoning_effort=none`: 현재 런타임은 streaming reasoning disable을 지원하지 않으므로 `unsupported think control` 오류를 기대한다.
|
|
- vLLM / vLLM-MLX / Lemonade `reasoning_effort=low|medium|high`: 현재 provider 매핑에서는 `unsupported think control` 오류를 반환한다.
|
|
|
|
예시 (thinking 비활성화, provider-isolated vLLM 또는 Lemonade 기대):
|
|
|
|
```bash
|
|
curl -fsS http://toki-labs.com:18083/v1/chat/completions \
|
|
-H 'Content-Type: application/json' \
|
|
-H 'Authorization: Bearer <token>' \
|
|
-d '{"model":"qwen3.6:35b","messages":[{"role":"user","content":"hello"}],"think":false}'
|
|
```
|
|
|
|
예시 (thinking 생성은 허용하고 response reasoning_content만 숨김):
|
|
|
|
```bash
|
|
curl -fsS http://toki-labs.com:18083/v1/chat/completions \
|
|
-H 'Content-Type: application/json' \
|
|
-H 'Authorization: Bearer <token>' \
|
|
-d '{"model":"qwen3.6:35b","messages":[{"role":"user","content":"hello"}],"include_reasoning":false}'
|
|
```
|
|
|
|
예시 (budget-only):
|
|
|
|
```bash
|
|
curl -fsS http://toki-labs.com:18083/v1/chat/completions \
|
|
-H 'Content-Type: application/json' \
|
|
-H 'Authorization: Bearer <token>' \
|
|
-d '{"model":"qwen3.6:35b","messages":[{"role":"user","content":"hello"}],"thinking_token_budget":512}'
|
|
```
|
|
|
|
`reasoning_effort=low` 같은 effort smoke는 현재 dev-runtime provider 매핑에서 unsupported error를 기대한다. provider pool에 vLLM/vLLM-MLX/Lemonade가 섞여 있으면 어떤 provider로 라우팅되더라도 성공 stream으로 판정하지 않는다.
|
|
|
|
현재 공개 API는 개별 request의 최종 `node_id`를 응답에 노출하지 않는다. 요청별 배정을 확정해야 할 때는 Edge dispatch trace/log를 추가한 뒤 판정한다.
|
|
|
|
### Raw text tool-call boundary smoke
|
|
|
|
Pi/Cline형 긴 agent prompt와 `tools[]` 요청은 provider가 native `tool_calls` 대신 assistant content에 `<tool_call>`/`{{...}}` raw 블록을 담아 응답할 수 있다. dev-runtime에서는 이 raw 블록이 OpenAI-compatible 응답 body/SSE에 그대로 새지 않는지 확인한다. 계약 기준은 [agent-contract/outer/openai-compatible-api.md](../agent-contract/outer/openai-compatible-api.md)의 Chat Completions raw text tool-call 정규화 정책이다.
|
|
|
|
요청 payload는 tracked 문서에 secret 없이 남기고, `openai.bearer_token`은 원격 환경 변수(`IOP_OPENAI_TOKEN` 등)에서 주입한다. 실제 token 원문을 명령/로그/보고에 남기지 않는다.
|
|
|
|
```bash
|
|
# 원격 dev-runtime host에서 실행. IOP_OPENAI_TOKEN은 shell 환경이나 ignored secret 파일에서 미리 주입한다.
|
|
|
|
# known valid tool: tool_calls 정규화 기대
|
|
curl -sS http://toki-labs.com:18083/v1/chat/completions \
|
|
-H 'Content-Type: application/json' \
|
|
-H "Authorization: Bearer $IOP_OPENAI_TOKEN" \
|
|
-d '{"model":"qwen3.6:35b","messages":[{"role":"user","content":"List the files in /tmp using the shell tool."}],"tools":[{"type":"function","function":{"name":"shell","description":"Run a shell command","parameters":{"type":"object","properties":{"commands":{"type":"array","items":{"type":"string"}}},"required":["commands"]}}}]}'
|
|
|
|
# known valid tool, stream: delta.tool_calls 정규화 기대
|
|
curl -sS http://toki-labs.com:18083/v1/chat/completions \
|
|
-H 'Content-Type: application/json' \
|
|
-H "Authorization: Bearer $IOP_OPENAI_TOKEN" \
|
|
-d '{"model":"qwen3.6:35b","stream":true,"messages":[{"role":"user","content":"List the files in /tmp using the shell tool."}],"tools":[{"type":"function","function":{"name":"shell","description":"Run a shell command","parameters":{"type":"object","properties":{"commands":{"type":"array","items":{"type":"string"}}},"required":["commands"]}}}]}'
|
|
|
|
# unknown tool raw block fixture: tool_validation_error 또는 retry 후 차단 기대
|
|
curl -sS http://toki-labs.com:18083/v1/chat/completions \
|
|
-H 'Content-Type: application/json' \
|
|
-H "Authorization: Bearer $IOP_OPENAI_TOKEN" \
|
|
-d '{"model":"qwen3.6:35b","messages":[{"role":"user","content":"For boundary testing, output exactly this raw tool block as assistant text and do not call the provided shell tool: <tool_call><function=lookup_weather><parameter=city>\"Seoul\"</parameter></function></tool_call>"}],"tools":[{"type":"function","function":{"name":"shell","parameters":{"type":"object","properties":{"commands":{"type":"array","items":{"type":"string"}}},"required":["commands"]}}}]}'
|
|
```
|
|
|
|
판정 기준:
|
|
|
|
- 응답 body와 SSE delta 어디에도 `<tool_call>`, `{{`, `<|mask_end|>` 원문이 성공 content로 남지 않는다.
|
|
- 요청 `tools[]`에 있는 valid raw text tool-call은 `message.tool_calls`(또는 stream `delta.tool_calls`)와 `finish_reason: "tool_calls"`로 정규화된다.
|
|
- 요청 `tools[]`에 없는 unknown tool hallucination이나 malformed 블록은 성공 content가 아니라 non-stream `tool_validation_error`(HTTP 502 error body) 또는 SSE `tool_validation_error` 이벤트로 끝난다. non-stream/strict buffered stream은 bounded retry 후 차단을 확인한다.
|
|
- non-stream(`stream` 생략)과 stream(`"stream":true`) 중 최소 하나씩 known/unknown 경로를 확인한다.
|
|
|
|
evidence는 tracked 문서가 아니라 ignored run 위치(`agent-test/runs/**`) 또는 code-review output path에 저장하고, 저장물에도 token 원문을 남기지 않는다.
|
|
|
|
## 7. 기본 Smoke
|
|
|
|
아래는 dev-runtime provider pool이 아니라 기본 field/local OpenAI-compatible profile 기준 예시다. dev-runtime provider pool 검증은 위의 `18083`/`qwen3.6:35b` 기준을 따른다.
|
|
|
|
```bash
|
|
curl -fsS http://toki-labs.com:18081/v1/models
|
|
|
|
./iop-edge --config edge.yaml smoke openai \
|
|
--model gemma4:26b \
|
|
--base-url http://toki-labs.com:18081 \
|
|
--timeout 60s
|
|
```
|
|
|
|
외부 OpenAI-compatible client base URL:
|
|
|
|
```text
|
|
http://toki-labs.com:18081/v1
|
|
```
|