161 lines
11 KiB
Markdown
161 lines
11 KiB
Markdown
---
|
|
test_env: dev
|
|
test_profile: edge-smoke
|
|
domain: edge
|
|
verification_type: smoke
|
|
last_rule_updated_at: 2026-07-04
|
|
---
|
|
|
|
# edge-smoke dev 테스트
|
|
|
|
## 읽기 조건
|
|
|
|
- `apps/edge/**` 변경 또는 edge registry, transport, service, console, OpenAI-compatible/A2A 입력 표면 dev 검증 판단이 필요한 경우
|
|
|
|
## 적용 범위
|
|
|
|
- `apps/edge/cmd/edge/**`
|
|
- `apps/edge/internal/bootstrap/**`
|
|
- `apps/edge/internal/transport/**`
|
|
- `apps/edge/internal/service/**`
|
|
- `apps/edge/internal/events/**`
|
|
- `apps/edge/internal/input/**`
|
|
- `apps/edge/internal/openai/**`
|
|
- `apps/edge/internal/opsconsole/**`
|
|
- `apps/edge/internal/node/**`
|
|
|
|
## 분류
|
|
|
|
- domain: edge
|
|
- verification_type: smoke
|
|
- scope: edge 실행 그룹, node registry, input surface baseline
|
|
|
|
## 환경
|
|
|
|
- host: local checkout. dev runtime, external CLI, shared port evidence가 필요하면 원격 runner를 사용한다.
|
|
- port: compose Edge-Node TCP transport `19003`; dev artifact/bootstrap HTTP 후보 `18082`, dev Edge OpenAI-compatible HTTP 후보 `18083`, dev Edge metrics 후보 `19101`.
|
|
- runtime: Go `1.24`
|
|
- package manager: Go modules / Makefile
|
|
- docker: unit/smoke quick check는 Docker를 요구하지 않는다. compose dev 검증은 `docker compose --env-file .env.dev.example ...`로 수행한다.
|
|
- external service: dev artifact/base URL 후보 `http://toki-labs.com:18082`, dev Edge runtime 주소 후보 `toki-labs.com:19003`
|
|
- model endpoint: dev OpenAI-compatible base URL 후보 `http://toki-labs.com:18083/v1`
|
|
- credential: token/secret 원문은 문서에 기록하지 않는다.
|
|
|
|
## dev-runtime provider pool 인벤토리
|
|
|
|
dev-runtime provider pool과 3-node 연결 상태를 점검할 때는 `agent-test/dev/inventory.yaml`의 machine-readable 값을 우선하고, 원격 runner `ssh toki@toki-labs.com`의 `/Users/toki/agent-work/iop-dev` checkout을 기준으로 한다.
|
|
|
|
- Edge config: `build/dev-runtime/edge.yaml`
|
|
- Edge id: `edge-toki-labs-dev`
|
|
- Control Plane HTTP: `http://127.0.0.1:18001`
|
|
- 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`
|
|
- model alias: `qwen3.6:35b`
|
|
|
|
노드 후보:
|
|
|
|
- mac CLI + MLX provider node: `mac-codex-node` / `mac-codex`
|
|
- SSH/user: `ssh toki@toki-labs.com`
|
|
- 목적: `cli` adapter, `codex app-server` + `mac-mlx-vllm` provider resource
|
|
- workspace: `/Users/toki/agent-work/iop-workspace/nomadcode`
|
|
- MLX vLLM provider: `mac-mlx-vllm`
|
|
- provider endpoint: `http://127.0.0.1:8002/v1`
|
|
- served model: `mlx-community/Qwen3.6-35B-A3B-4bit`
|
|
- capacity baseline: `2`
|
|
- priority baseline: `2`
|
|
- workdir: `/Users/toki/agent-work/iop-mlx-vllm`
|
|
- runtime baseline: `vllm-mlx`, `--max-num-seqs 2`, `--max-kv-size 262144`, `--max-request-tokens 262144`, `--use-paged-cache --paged-cache-block-size 64 --max-cache-blocks 4096`
|
|
- KV policy: per-call window bound `262144`, one full context-size setting
|
|
- long-context admission baseline: `total_context_tokens=262144`, `long_context_capacity=1` (KV budget `262144` fits one full 262144-window request; long slot count is not the normal capacity `2`)
|
|
- GX10 vLLM node: `gx10-vllm-node` / `gx10-vllm`
|
|
- SSH/user: `ssh toki@192.168.0.91`
|
|
- provider endpoint: `http://192.168.0.91:8001/v1`
|
|
- served model: `nvidia/Qwen3.6-35B-A3B-NVFP4`
|
|
- capacity baseline: `4`
|
|
- priority baseline: `0`
|
|
- runtime baseline: `vllm`, `--max-model-len 262144`, `--max-num-seqs 4`, `--gpu-memory-utilization 0.30`, `--reasoning-parser qwen3`
|
|
- long-context admission baseline: `total_context_tokens=262144`, `long_context_capacity=1` (conservative; `max-model-len 262144` until runtime evidence proves a larger concurrent long-context budget)
|
|
- workspace: `/home/toki/iop-gx10-vllm`
|
|
- OneXPlayer Lemonade node: `onexplayer-lemonade-node` / `onexplayer-lemonade`
|
|
- SSH/user: `ssh r0bin@192.168.0.59`
|
|
- 접속 기준: 현재 작업 호스트에서 직접 SSH
|
|
- provider endpoint: `http://192.168.0.59:13305/v1`
|
|
- served model: `Qwen3.6-35B-A3B-MTP-GGUF`
|
|
- capacity baseline: `3`
|
|
- priority baseline: `1`
|
|
- load baseline: backend `vulkan`, ctx size `524288`, `llamacpp_args="--spec-type none -np 3 -cb -fa on -b 4096 -ub 1024"`, `save_options=true`
|
|
- long-context admission baseline: `total_context_tokens=524288`, `long_context_capacity=2` (SDD D04 fixed; `ctx_size=524288`, `-np 3` 유지, `ctx_size`를 `524288` 초과로 키우지 않는다)
|
|
- workspace: `C:/Users/r0bin/iop-field`
|
|
|
|
OneXPlayer Lemonade는 `Qwen3.6-35B-A3B-MTP-GGUF` artifact를 사용하되 runtime MTP speculative decoding은 켜지 않는다. dev long-context baseline은 `/v1/load`에 `model_name=Qwen3.6-35B-A3B-MTP-GGUF`, `llamacpp_backend=vulkan`, `ctx_size=524288`, `--spec-type none -np 3 -cb -fa on -b 4096 -ub 1024`를 `save_options=true`로 저장한 상태다. llama.cpp backend의 `/slots`에서는 총 ctx `524288`이 slot 3개로 나뉘어 2026-07-04 기준 slot별 `n_ctx=174848`로 보인다.
|
|
|
|
OneXPlayer Lemonade Node는 원격 runner나 Edge host에서 다시 SSH하거나 proxy process로 띄우지 않는다. 현재 작업 호스트에서 OneXPlayer Windows host에 `ssh r0bin@192.168.0.59`로 직접 접속한 뒤 generated PowerShell bootstrap을 실행한다.
|
|
|
|
mac-mlx-vllm provider는 mac-codex-node 소속 resource로, Edge host와 같은 macOS host에서 vllm-mlx process로 실행한다. vllm-mlx API는 외부에 직접 노출하지 않고 `127.0.0.1:8002`에 bind하며, Edge OpenAI-compatible adapter가 local bearer header로 호출한다. 운영 파일은 `/Users/toki/agent-work/iop-mlx-vllm/vllm-mlx.pid`, `logs/vllm-mlx.stdout.log`, `logs/vllm-mlx.stderr.log`를 기준으로 한다. Docker와 macOS 여유 메모리를 고려해 capacity는 `2`를 기본선으로 유지한다.
|
|
|
|
## 명령
|
|
|
|
- setup:
|
|
- lint:
|
|
- unit: `go test ./apps/edge/...`
|
|
- smoke: `./scripts/e2e-smoke.sh`는 기본 포트 임시 설정을 쓰는 보조 smoke이므로 dev 포트 override 필요 여부를 먼저 확인한다.
|
|
- e2e: `make test-e2e`는 보조 smoke이며 full-cycle 실제 구동을 대체하지 않는다.
|
|
- model: dev OpenAI-compatible profile을 띄운 경우 `iop-edge smoke openai --base-url http://127.0.0.1:18083`
|
|
- full-cycle: repo 내부 edge-node 진단, `iop-edge smoke openai`, OpenAI-compatible 입력 표면 수동 검증
|
|
|
|
## 필수 검증
|
|
|
|
- 변경한 edge 패키지 또는 `go test ./apps/edge/...`를 실행한다.
|
|
- registry, service, transport, console, HTTP/A2A 입력 표면을 바꾼 경우 edge-node 메시지 2회 왕복과 command 응답을 확인한다.
|
|
- OpenAI-compatible 경계를 바꾼 경우 dev `18083` 기준 `iop-edge smoke openai` 또는 동등한 `/healthz`, `/v1/models`, `/v1/responses` 확인으로 edge service와 node adapter 경로 수렴을 확인한다.
|
|
- OpenAI-compatible tool-call 경계(`apps/edge/internal/openai/**`의 text tool-call 합성/validation)를 바꾼 경우 dev `18083` `/v1/chat/completions`에 Pi/Cline형 `tools[]` 요청을 non-stream/stream 각각 최소 1회 보내 raw text tool-call boundary smoke를 수행한다. token은 원격 환경 변수에서 주입하고 원문을 명령/로그/보고에 남기지 않는다.
|
|
- bootstrap/artifact 경계를 바꾼 경우 dev artifact/base URL 후보 `18082`가 local/test `18080` field baseline을 덮어쓰지 않는지 확인한다.
|
|
|
|
## 보조 검증
|
|
|
|
- `./scripts/e2e-smoke.sh`는 edge-node 최소 생존 확인에 사용한다.
|
|
- `./scripts/e2e-openai-ollama.sh`는 OpenAI-compatible Ollama 입력 표면 보조 확인에 사용한다.
|
|
|
|
## 판정 기준
|
|
|
|
- node 등록, `/nodes`, console 메시지 전송, 기대 payload를 포함한 `[node-*-message]` 출력, 같은 run의 complete event가 확인된다.
|
|
- node 로컬 `[node-message]` payload 라인 목록과 edge `[node-*-message]` payload 라인 목록이 run별로 내용/순서까지 동일해야 한다.
|
|
- edge complete는 같은 run의 마지막 `[node-*-message]` 이후에만 정상이다.
|
|
- OpenAI-compatible smoke에서 `/healthz`, `/v1/models`, `/v1/responses`가 기대 상태로 응답한다.
|
|
- raw text tool-call boundary smoke: Pi/Cline형 `tools[]` 요청에서 응답 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"`로 정규화되고, unknown tool hallucination이나 malformed 블록은 성공 content가 아니라 non-stream `tool_validation_error`(HTTP 502) 또는 SSE `tool_validation_error` 이벤트로 끝난다. non-stream/strict buffered stream은 bounded retry 후 차단을 확인한다. 세부 payload와 계약은 `docs/edge-local-dev-guide.md`의 Raw text tool-call boundary smoke와 `agent-contract/outer/openai-compatible-api.md`를 따른다.
|
|
- raw boundary smoke evidence는 tracked 문서가 아니라 ignored run 위치(`agent-test/runs/**`)나 code-review output path에 저장하고, 저장물에도 token 원문을 남기지 않는다.
|
|
- provider-pool dispatch는 `in_flight >= capacity`인 provider를 후보에서 제외하고, 남은 후보 중 가장 낮은 `in_flight` 레벨을 먼저 선택한다. 같은 `in_flight` 레벨 안에서는 낮은 priority 값과 rotation으로 분산한다.
|
|
- dev-runtime capacity smoke는 `/v1/responses`와 `/v1/chat/completions` 각각에 provider capacity 총합 + 1개 동시 요청을 보내고, Control Plane status의 `provider_snapshots`에서 총 `in_flight`가 capacity 총합에 도달하며 `queued`가 1 이상 잡히는지 확인한다. 현재 `gx10-vllm=4`, `onexplayer-lemonade=3`, `mac-mlx-vllm=2`이면 endpoint별 10개 동시 요청에서 총 `in_flight=9`, `queued>=1` 관측을 기준으로 한다. 13개 동시 확장 smoke에서는 총 `in_flight=9`, `queued>=4` 관측을 기대한다.
|
|
- capacity smoke 완료 후 대상 provider의 `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`개, 최종 회복 `in_flight=0`, `queued=0`이 관측되었다. 현재 Mac capacity `2` 기준 13-way 확장 smoke 기대치는 peak `in_flight=9`, queue 대기 `4`개 이상이다.
|
|
- Qwen provider-pool smoke는 thinking/reasoning 텍스트가 포함될 수 있다. 추론 출력 자체를 실패로 보지 말고 HTTP 성공, model alias, final marker 포함 여부, provider node log/run count 증가로 판정한다. 응답 전체가 특정 token과 정확히 같은지 비교하는 strict exact-match는 이 profile의 기본 판정으로 쓰지 않는다.
|
|
- bootstrap 사용자 명령은 완성된 URL과 positional token 하나만 포함한다.
|
|
|
|
## 기준 출력 예시
|
|
|
|
```text
|
|
edge> /nodes
|
|
[edge] sent run_id=...
|
|
[node-*-message] IOP_EXPECTED_REPLY
|
|
[node-*-event] complete
|
|
```
|
|
|
|
## 차단 기준
|
|
|
|
- dev host 또는 Edge-Node TCP port 접근이 불가능하다.
|
|
- 외부 CLI profile 검증에 필요한 CLI 설치, 계정, provider 상태가 없다.
|
|
- dev artifact/model/metrics 포트가 기존 field baseline과 충돌한다.
|
|
|
|
## 보고 항목
|
|
|
|
- 실행한 명령:
|
|
- 성공한 검증:
|
|
- 실패/차단된 검증:
|
|
- 생략 사유:
|
|
- 남은 위험:
|
|
|
|
## 금지 사항
|
|
|
|
- secret, token, 개인 endpoint 원문은 tracked 파일에 기록하지 않는다.
|
|
- field baseline 포트(`18080`, `18081`, `19090`, `19092`)를 dev 전용 포트로 재사용하지 않는다.
|