diff --git a/agent-ops/rules/project/rules.md b/agent-ops/rules/project/rules.md index bb4bc4d..2d0fa07 100644 --- a/agent-ops/rules/project/rules.md +++ b/agent-ops/rules/project/rules.md @@ -94,6 +94,7 @@ ## 스킬 라우팅 +- dev 배포, dev-runtime 배포, Edge/Node dev 환경 배포, provider pool 배포, OpenAI-compatible capacity smoke 검증: `agent-ops/skills/project/dev-runtime-deploy/SKILL.md` - 사용자 실행 파이프라인 검증, repo 내부 edge-node 진단, 메시지 2회 왕복, edge command 응답, 보조 E2E smoke, full-cycle 실제 구동, `scripts/dev/edge.sh`/`scripts/dev/node.sh` 진단 테스트: `agent-ops/skills/project/e2e-smoke/SKILL.md` - field 테스트 포트, artifact/bootstrap HTTP, 외부 테스트 환경: `agent-test/local/rules.md`를 따른다. - bootstrap/install UX, Agent Bootstrap, specialized agent 등록, Control Plane enrollment: `testing` domain rule과 `agent-test/local/rules.md`를 따른다. diff --git a/agent-ops/skills/project/dev-runtime-deploy/SKILL.md b/agent-ops/skills/project/dev-runtime-deploy/SKILL.md new file mode 100644 index 0000000..04aa65e --- /dev/null +++ b/agent-ops/skills/project/dev-runtime-deploy/SKILL.md @@ -0,0 +1,130 @@ +--- +name: dev-runtime-deploy +version: 1.0.0 +description: dev 배포, dev-runtime 배포, Edge/Node dev 환경 배포 요청에서 clean sync, 전체 테스트, rebuild, 원격 배포, OpenAI-compatible capacity smoke를 수행하는 절차 +--- + +# dev-runtime-deploy + +## 목적 + +dev-runtime provider pool을 제품 검증이 끝난 상태로 배포한다. 배포는 원격 checkout 동기화, 전체 테스트, 전체 rebuild, Edge/Node 재시작, provider snapshot 기반 capacity 검증까지 포함한다. + +## 언제 호출할지 + +- 사용자가 `dev 배포`, `dev-runtime 배포`, `배포해봐`처럼 dev 환경 배포를 요청할 때 +- Edge, mac CLI node, GX10 vLLM node, OneXPlayer Lemonade node로 이루어진 dev-runtime provider pool을 갱신할 때 +- OpenAI-compatible `/v1/responses` 또는 `/v1/chat/completions` 경로가 provider capacity만큼 채워지는지 검증할 때 +- dev 환경의 remote runner, node 접속 정보, provider capacity, model alias 변경을 배포 절차에 반영할 때 + +## 입력 + +- `env`: 배포 대상 환경. 기본값은 `dev`이다. +- `model`: OpenAI-compatible model alias. 지정하지 않으면 dev 환경 문서의 기준 model을 사용한다. +- `capacity_targets`: provider별 기대 capacity. 지정하지 않으면 dev 환경 인벤토리 또는 `agent-test/dev/*` 문서에서 읽는다. +- `source_ref`: 배포할 git ref. 지정하지 않으면 원격 runner의 기본 배포 branch 기준을 따른다. + +## 먼저 확인할 것 + +- [ ] `agent-ops/rules/project/domain/testing/rules.md`를 읽고 사용자 실행 파이프라인 검증 기준을 확인한다. +- [ ] dev 환경은 `agent-test/dev/inventory.yaml`을 먼저 읽는다. 파일이 없으면 `agent-test/dev/rules.md`, `agent-test/dev/edge-smoke.md`, `agent-test/dev/node-smoke.md`로 fallback하고, 구조화 inventory 누락을 보고한다. +- [ ] dev-runtime provider pool과 compose/local profile을 섞지 않는다. dev-runtime은 native Edge/Node runtime과 dev-runtime config를 기준으로 한다. +- [ ] OneXPlayer SSH 접속 정보는 현재 host 기준 `ssh r0bin@192.168.0.59`이다. `toki` 사용자명 또는 remote runner를 경유한 OneXPlayer 접속을 사용하지 않는다. +- [ ] 현재 구현의 completion 검증 대상은 legacy `/v1/completions`가 아니라 `/v1/chat/completions`이다. `/v1/completions`는 route가 구현되어 있을 때만 별도 검증한다. +- [ ] token, secret header, bootstrap token, private key 경로는 최종 보고에 원문으로 출력하지 않는다. + +## 실행 절차 + +1. **환경 인벤토리 확정** + - 배포 대상 runner, repo path, Edge id, Control Plane status URL, OpenAI base URL, Edge admin URL, Node SSH 정보를 문서에서 확정한다. + - provider pool 대상 model alias와 provider별 capacity를 확정한다. + - 필수 정보가 없거나 서로 충돌하면 배포를 시작하지 말고 누락/충돌 항목을 보고한다. + +2. **원격 checkout clean sync** + - 원격 runner checkout에서 `git fetch` 후 배포 기준 ref로 `git reset --hard`를 수행한다. + - dirty 파일은 보존 대상으로 보지 않는다. 배포 전 clean 상태를 만든다. + - 기본 cleanup은 `git clean -fd`이다. `git clean -fdx`는 config, token, secret, runtime artifact까지 삭제할 수 있으므로 사용하지 않는다. + - sync 후 `git status --short --branch`와 `git log --oneline -1`을 기록한다. + +3. **빌드 전 전체 테스트** + - clean source 기준으로 `go test ./...`를 실행한다. + - client/Flutter, proto, Makefile, script, config 변경이 배포 범위에 포함되면 해당 도메인 규칙의 전체 테스트도 추가한다. + - 전체 테스트가 실패하면 build/deploy를 진행하지 않고 실패 패키지와 핵심 오류를 보고한다. + +4. **전체 rebuild** + - 같은 source ref에서 dev-runtime Edge binary, mac node binary, Linux ARM64 node binary, Windows AMD64 node binary를 모두 다시 빌드한다. + - stale binary가 의심되거나 `config refresh` subcommand, admin port, version 출력이 맞지 않으면 clean sync부터 다시 시작한다. + - 빌드 산출물 경로, timestamp, 크기, 실행 가능 여부를 기록한다. + +5. **빌드 후 기본 동작 테스트** + - 빌드된 Edge binary로 `config check`, `config refresh --help`, `config refresh --mode dry-run`을 실행한다. + - 빌드 후에도 `go test ./...`를 실행한다. 실행하지 못하면 사유와 남은 위험을 보고한다. + - dry-run이 `rejected` 또는 예상 밖 `restart_required`를 반환하면 배포를 멈추고 config diff를 보고한다. + +6. **배포와 재시작** + - Edge와 mac CLI node는 원격 runner에서 빌드 산출물 기준으로 재시작한다. + - GX10 node는 Linux ARM64 node binary를 배포하고 기존 node process를 재시작한다. + - OneXPlayer node는 현재 host에서 `ssh r0bin@192.168.0.59`로 접속한다. artifact가 remote runner에 있으면 현재 host를 통해 전달한 뒤 Windows host에서 교체한다. + - OneXPlayer에서는 SSH 세션 안의 `Start-Process`로 장기 실행을 시작하지 않는다. `Win32_Process.Create` 또는 동등한 세션 독립 실행 방식으로 `iop-node.exe --config node.yaml serve`를 시작한다. + +7. **배포 후 연결 검증** + - Edge, OpenAI-compatible listener, Node TCP, admin port, Control Plane status port가 열려 있는지 확인한다. + - Control Plane status에서 Edge가 connected이고 dev-runtime 기준 3개 node가 connected인지 확인한다. + - 각 node의 `provider_snapshots`에서 provider `id`, `capacity`, `in_flight`, `queued`, `health`, `served_models`를 확인한다. + - `/v1/models`가 대상 model alias를 노출하는지 확인한다. + +8. **OpenAI-compatible capacity smoke** + - `/v1/responses`와 `/v1/chat/completions`를 각각 검증한다. legacy `/v1/completions`는 구현되어 있지 않으면 실패로 보지 않는다. + - 표준 부하 프롬프트는 짧은 토큰 응답을 요구하지 않는다. 700~1200 token 수준의 구조화된 답변을 유도해 요청이 동시에 관측될 시간을 만든다. + - endpoint별로 총 provider capacity + 1개 요청을 동시에 보낸다. 예: capacity가 `4 + 3 = 7`이면 8개 동시 호출을 보낸다. + - 환경 문서에 더 작은 aggregate smoke 기준이 남아 있더라도, 배포 완료 판정은 이 capacity+1 기준을 우선한다. + - 요청 실행 중 Control Plane status를 반복 polling하여 대상 provider들의 `in_flight` 합이 총 capacity에 도달하고 `queued` 합이 1 이상이 되는 순간을 증거로 남긴다. + - 각 provider의 `in_flight`가 자기 capacity를 넘지 않고, 적어도 한 번은 기대 capacity까지 차는지 확인한다. + - 모든 요청 완료 후 같은 status에서 대상 provider들의 `in_flight=0`, `queued=0`으로 돌아오는지 확인한다. + - Qwen 계열 reasoning/thinking 텍스트는 정상 응답으로 허용한다. exact-output match를 smoke 성공 기준으로 삼지 않는다. + +9. **결과 보고** + - source ref, clean sync 결과, 테스트 결과, 빌드 산출물, process/port 상태, connected node 목록, provider capacity snapshot, capacity smoke 관측값을 보고한다. + - 실패한 단계가 있으면 다음 단계를 진행했는지 여부를 명확히 구분한다. + - capacity smoke가 타이밍 문제로 관측 실패했으면 요청 성공과 별도로 `capacity 관측 미충족`으로 보고하고, 프롬프트 길이 또는 status polling 간격 조정을 제안한다. + +## 실행 결과 검증 + +- [ ] 원격 runner checkout이 배포 기준 ref로 clean sync되었는가 +- [ ] 빌드 전 `go test ./...`와 필요한 추가 전체 테스트가 통과했는가 +- [ ] dev-runtime Edge/mac/Linux ARM64/Windows AMD64 binary가 같은 source ref에서 rebuild되었는가 +- [ ] 빌드 후 config check, refresh help, refresh dry-run, 전체 테스트가 통과했는가 +- [ ] Edge, mac CLI node, GX10 vLLM node, OneXPlayer Lemonade node가 재시작되고 3개 node가 connected 상태인가 +- [ ] OneXPlayer 접속과 실행이 `r0bin@192.168.0.59` 및 세션 독립 실행 방식으로 수행되었는가 +- [ ] `/v1/responses` capacity smoke에서 총 capacity만큼 `in_flight`가 차고 초과 요청이 queue에 잡혔는가 +- [ ] `/v1/chat/completions` capacity smoke에서 총 capacity만큼 `in_flight`가 차고 초과 요청이 queue에 잡혔는가 +- [ ] 완료 후 provider `in_flight=0`, `queued=0`으로 회복되었는가 +- 검증 실패 시: 실패 단계, 실패한 host/provider/endpoint, 관측된 snapshot, 진행 중단 여부를 보고한다. + +## 출력 형식 + +```text +dev-runtime 배포 결과 +- Source: , clean= +- Pre-build tests: - +- Build: edge=, mac-node=, linux-arm64-node=, windows-amd64-node= +- Post-build checks: config-check=, refresh-help=, refresh-dry-run=, tests= +- Deployment: edge=, mac-node=, gx10=, onexplayer= +- Ports: +- Nodes: +- Providers: +- OpenAI-compatible: models=, responses-capacity=, chat-completions-capacity= +- Capacity evidence: +- Blockers/Risk: <없음 또는 내용> +``` + +## 금지 사항 + +- dirty checkout이나 stale binary를 그대로 배포하지 않는다. +- `git clean -fdx`를 기본 cleanup으로 사용하지 않는다. +- 빌드 전 전체 테스트 실패 후 배포를 계속하지 않는다. +- OneXPlayer에 `toki` 사용자명으로 접속하거나 remote runner에서 다시 OneXPlayer로 SSH 접속하지 않는다. +- OneXPlayer 장기 실행을 Windows OpenSSH 세션 내부 `Start-Process`에 의존하지 않는다. +- Qwen reasoning/thinking 출력을 실패로 판정하거나 exact-output smoke를 기본 성공 기준으로 삼지 않는다. +- OpenAI-compatible 요청 성공만으로 capacity 검증 성공을 선언하지 않는다. provider snapshot의 `in_flight/queued` 관측을 함께 남긴다. +- 이 프로젝트 전용 배포 절차를 `agent-ops/rules/common/_templates` 또는 common skill에 넣지 않는다. diff --git a/agent-test/README.md b/agent-test/README.md new file mode 100644 index 0000000..032d13c --- /dev/null +++ b/agent-test/README.md @@ -0,0 +1,56 @@ +# agent-test 환경 확장 기준 + +이 디렉터리는 테스트 환경별 실행 기준과 도메인/검증 시나리오별 profile을 둔다. +새 환경(`qa`, `staging`, `dev-gpu`, `dev-team-a` 등)을 추가할 때는 기존 환경을 임의로 복사하지 말고 이 기준과 `_templates/`를 먼저 따른다. + +## 환경 디렉터리 + +- 환경 디렉터리는 `agent-test//` 형식을 사용한다. +- ``는 소문자 영문, 숫자, 하이픈만 사용한다. +- 각 환경에는 반드시 `agent-test//rules.md`를 둔다. +- 도메인/검증 시나리오별 문서는 `agent-test//.md` 형식을 사용한다. +- 기본 도메인 smoke profile은 기존 도메인 이름을 유지해 `node-smoke.md`, `edge-smoke.md`, `control-plane-smoke.md`, `client-smoke.md`, `platform-common-smoke.md`, `testing-smoke.md`처럼 둔다. + +## rules.md 표준 섹션 + +새 `rules.md`는 아래 섹션 순서를 유지한다. + +1. `공통 규칙` +2. `기본 환경` +3. `포트 매핑` +4. `런타임 프로필` +5. `프리플라이트` +6. `노드/Provider 인벤토리 위치` +7. `Field/bootstrap 반복 테스트 기준` +8. `라우팅` +9. `라우팅 규칙` + +섹션이 해당 환경에 직접 필요하지 않더라도 삭제하지 말고 `없음`, `해당 없음`, 또는 대상 profile 링크를 남긴다. + +## 확장 원칙 + +- 환경 공통값은 `rules.md`에 둔다. +- 실제 노드, provider, bootstrap, model endpoint, workspace, SSH user/IP 같은 상세 인벤토리는 agent가 구조적으로 읽어야 하면 `inventory.yaml`에 두고, 사람용 설명과 도메인별 판정 기준은 해당 도메인 profile에 둔다. +- 상위 `rules.md`에는 상세값을 복제하지 말고 `노드/Provider 인벤토리 위치`와 `라우팅`으로 연결한다. +- compose, native Edge, dev-runtime provider pool처럼 같은 환경 안의 실행 모드는 `런타임 프로필`에서 분리한다. +- 같은 포트 이름은 모든 환경에서 같은 행 이름을 쓴다. 값이 없으면 `해당 없음` 또는 `후보 `로 둔다. +- shared remote runner를 쓰는 환경은 `repo root`, `sync 기준`, `프리플라이트`를 반드시 명시한다. +- secret, token, API key, private credential 원문은 `agent-test/`, `docs/`, `agent-roadmap/`에 기록하지 않는다. +- private IP와 SSH user는 테스트 접속에 필요한 환경값이면 기록할 수 있다. 단, 잘못된 user가 발견되면 해당 env rules/profile과 사람용 guide를 함께 정정한다. +- `agent-test/local/`은 local/private 기준이라 git 추적 제외될 수 있다. 추적 여부와 상관없이 현재 workspace의 운영 기준으로는 같은 구조를 유지한다. + +## 새 환경 추가 절차 + +1. `_templates/env-rules-template.md`를 `agent-test//rules.md`로 복사한다. +2. ``, 날짜, runner, repo root, 포트, runtime profile을 채운다. +3. agent가 반복해서 읽어야 하는 host/node/provider 값이 있으면 `_templates/inventory-template.yaml`을 사용해 `agent-test//inventory.yaml`을 만든다. +4. 기존 도메인 smoke profile이 필요하면 `_templates/test-profile-template.md`를 사용해 `agent-test//-smoke.md`를 만든다. +5. `rules.md`의 `노드/Provider 인벤토리 위치`와 `라우팅`에 생성한 inventory와 profile을 연결한다. +6. remote runner나 external provider를 쓰면 `프리플라이트`에 확인할 항목을 구체화한다. +7. local/test/dev 등 기존 환경과 공존해야 하면 `포트 매핑`에 충돌하지 않는 값을 기록한다. +8. `rg --no-ignore`로 잘못된 SSH user, workspace, port 재사용, secret 원문이 없는지 확인한다. + +## 현재 표준 구현 + +- `agent-test/local/rules.md` +- `agent-test/dev/rules.md` diff --git a/agent-test/_templates/env-rules-template.md b/agent-test/_templates/env-rules-template.md new file mode 100644 index 0000000..f68b358 --- /dev/null +++ b/agent-test/_templates/env-rules-template.md @@ -0,0 +1,101 @@ +--- +test_env: +last_rule_updated_at: +--- + +# 테스트 규칙 + +**현재 문서를 반드시 끝까지 정독하고 작업한다. 다 읽지 않고 즉각 작업은 금지한다.** +** 테스트 환경의 핵심 실행 모드와 local/dev/qa 등 다른 환경과의 공존 기준을 이 문서에 둔다.** + +## 공통 규칙 + +- 테스트/검증은 이 파일을 기준으로 판단한다. +- 작업 완료 검증은 변경 범위 기준으로 선택한다. +- 필수 검증을 실행하지 못하면 차단 사유로 보고한다. +- 최종 보고에는 실행 명령, 결과, 생략 사유, 남은 위험을 남긴다. +- 환경값, secret, 개인 endpoint는 tracked docs/roadmap에 쓰지 않는다. +- token/secret 원문은 원격 환경에서 주입하고, shell stdout/stderr와 최종 보고에 출력하지 않는다. +- 테스트용 Docker 환경을 작성/수정할 때는 Docker Compose `networks`, IPAM subnet, static IPv4, `extra_hosts`, host publish 문서 어디에도 host LAN/VPN과 충돌하는 대역을 사용하지 않는다. + +## 기본 환경 + +- host: <기본 실행 host 또는 runner SSH> +- repo root: <원격/로컬 repo root> +- sync 기준: +- env file: +- compose identity: +- port: <핵심 포트 요약> +- optional field ports: <선택 포트 또는 해당 없음> +- runtime: +- package manager: +- docker: +- external service: <외부 서비스 URL/host 요약> +- model endpoint: +- credential: secret/token/API key 원문은 문서에 기록하지 않는다. + +## 포트 매핑 + +| 용도 | local/test | dev | | +|---|---:|---:|---:| +| Web preview | `13000-13099` | `13001` | `` | +| Control Plane HTTP | `18000` | `18001` | `` | +| CP Client WS | `19080` | `19001` | `` | +| CP-Edge wire | `19081` | `19002` | `` | +| Edge-Node TCP | `19090` | `19003` | `` | +| Edge artifact/bootstrap | `18080` | `18082` | `` | +| Edge OpenAI-compatible | `18081` | `18083` | `` | +| Edge metrics | `19092` | `19101` | `` | +| PostgreSQL host publish | `15400` | `15401` | `` | +| Redis host publish | `16300` | `16301` | `` | + +<환경 안에서 compose/native/dev-runtime처럼 Edge-Node TCP가 갈라지면 여기에 명시한다.> + +## 런타임 프로필 + +- local quick check: 현재 checkout에서 가능한 quick check를 우선 실행한다. +- remote runner: +- compose stack: +- native/direct Edge profile: <필요 시 artifact/openai/metrics/edge-node 포트> +- provider pool profile: <필요 시 config path, model alias, Edge-Node TCP> +- external provider field: <필요 시 SSH/user/IP/provider endpoint> + +## 프리플라이트 + +- plan 작성 시 필수 검증이 현재 checkout을 벗어나 원격 runner, field/bootstrap, 외부 provider, Docker/code-server, emulator/device, 공유 장기 runtime을 사용하면 먼저 테스트 환경 프리플라이트를 계획에 기록한다. +- 테스트 환경 프리플라이트에는 runner, repo root/workdir, branch/HEAD/dirty 상태, local 변경과 원격 source 동기화 여부, binary/artifact 경로와 필요한 help/version 출력, config path, runtime identity, port/process 상태, 외부 host, OS/arch 가정을 포함한다. +- 프리플라이트에서 dirty/divergent checkout, stale artifact, 잘못된 identity, missing command, closed port, host OS 불일치, source 미동기화가 확인되면 plan은 먼저 setup/sync/rebuild 단계를 만들거나 blocker로 보고한다. remote deploy 환경에서 dirty 변경을 보존하지 않는 정책이면 배포 전 clean sync와 rebuild를 기본 단계로 둔다. profile 값을 이미 참이라고 가정한 검증 명령만 쓰지 않는다. + +## 노드/Provider 인벤토리 위치 + +- machine-readable 인벤토리 기준: `agent-test//inventory.yaml` 또는 `해당 없음` +- Edge/OpenAI-compatible 입력 표면 상세 기준: `agent-test//edge-smoke.md` +- Node 접속과 bootstrap 상세 기준: `agent-test//node-smoke.md` +- 공통 provider config 계약 기준: `agent-test//platform-common-smoke.md` +- compose stack 기준: `agent-test//control-plane-smoke.md`, `agent-test//client-smoke.md`, `agent-test//testing-smoke.md` + +## Field/bootstrap 반복 테스트 기준 + +- field/bootstrap 검증은 이 환경의 runner, checkout, artifact 기준으로 수행한다. 세부 Edge/Node profile은 해당 env의 `edge-smoke.md`와 `node-smoke.md`를 따른다. +- compose stack과 native/provider-pool profile이 서로 다른 Edge-Node TCP를 쓰면 두 프로필을 섞어서 판정하지 않는다. +- Node bootstrap은 Edge의 `node register`가 출력한 OS별 완성 명령을 그대로 사용한다. 사용자에게 안내하는 명령에는 임의 placeholder, 수동 token 치환, `IOP_*=` named environment parameter를 넣지 않는다. +- token, API key, private credential 원문은 tracked docs, roadmap, 테스트 규칙에 기록하지 않는다. 실행 증거에는 token을 마스킹하거나 명령 생성 사실만 남긴다. +- Linux/macOS Node는 생성된 `curl | bash` 계열 명령을 사용한다. Windows Node는 native PowerShell bootstrap을 기본으로 사용한다. +- provider capacity, model/provider mapping의 live apply는 해당 Edge binary가 `config refresh`를 제공하는 경우에만 사용한다. refresh 지원이 필요한 환경에서 subcommand나 admin port가 없으면 stale binary로 보고 clean sync/rebuild를 먼저 수행한다. +- Edge process restart 또는 일시 단절 후에는 Node reconnect 정책을 검증한다. retry 한계를 넘겨 Node process가 종료된 경우에만 해당 Node host에서 새 bootstrap 실행이 필요하다. + +## 라우팅 + +- node / smoke / node 실행 파이프라인 baseline: `agent-test//node-smoke.md` +- edge / smoke / edge 실행 그룹과 입력 표면 baseline: `agent-test//edge-smoke.md` +- control-plane / smoke / control-plane health와 wire baseline: `agent-test//control-plane-smoke.md` +- client / smoke / Flutter client와 IOP console package baseline: `agent-test//client-smoke.md` +- platform-common / smoke / 공통 설정과 protobuf 계약 baseline: `agent-test//platform-common-smoke.md` +- testing / smoke / 테스트 도구와 full-cycle 검증 baseline: `agent-test//testing-smoke.md` + +## 라우팅 규칙 + +- 여러 항목이 맞으면 모두 읽는다. +- 도메인 매핑이나 domain rule이 있으면 각 도메인의 `-smoke` 문서를 기본 baseline으로 둔다. +- 도메인이 아직 없을 때만 `project-smoke`를 fallback baseline으로 둔다. +- 도메인/검증 시나리오별 문서는 다른 테스트 문서로 라우팅하지 않는다. diff --git a/agent-test/_templates/inventory-template.yaml b/agent-test/_templates/inventory-template.yaml new file mode 100644 index 0000000..210d880 --- /dev/null +++ b/agent-test/_templates/inventory-template.yaml @@ -0,0 +1,57 @@ +test_env: +profile: +last_updated_at: + +source: + remote_runner: + ssh: + repo_root: + clean_sync: + - + dirty_policy: + +edge: + id: + config_path: + control_plane_http: + control_plane_status_url: + bootstrap_http_public: + openai_base_url_public: + openai_base_url_runner: + edge_node_tcp_public: + admin_addr_runner: + +build: + binaries: + edge: + node_macos: + node_linux_arm64: + node_windows_amd64: + +model: + alias: + provider_capacity_total: + capacity_smoke: + endpoints: + - /v1/responses + - /v1/chat/completions + concurrent_requests: capacity_plus_one + expected_total_in_flight: + expected_min_queued: 1 + prompt_policy: long_reasoning_allowed + exact_output_match: false + +nodes: + - id: + alias: + role: + ssh: + ssh_origin: + workspace: + provider_pool_candidate: + provider: + id: + type: + endpoint: + served_model: + capacity: diff --git a/agent-test/_templates/test-profile-template.md b/agent-test/_templates/test-profile-template.md new file mode 100644 index 0000000..c3fc54f --- /dev/null +++ b/agent-test/_templates/test-profile-template.md @@ -0,0 +1,79 @@ +--- +test_env: +test_profile: +domain: +verification_type: +last_rule_updated_at: +--- + +# 테스트 + +## 읽기 조건 + +- <이 도메인/검증 시나리오의 테스트, 검증, 실행 조건 판단이 필요한 경우> + +## 적용 범위 + +- <검증 대상 entrypoint, command, API, UX, profile, service> + +## 분류 + +- domain: +- verification_type: +- scope: <검증 대상 변경 범위 요약> + +## 환경 + +- host: +- port: +- runtime: +- package manager: +- docker: +- external service: +- model endpoint: +- credential: secret/token/API key 원문은 문서에 기록하지 않는다. + +## 명령 + +- setup: +- lint: +- unit: +- smoke: +- e2e: +- model: +- full-cycle: + +## 필수 검증 + +- <작업 완료 전 반드시 실행하거나 판단해야 하는 검증> + +## 보조 검증 + +- <실행하면 좋지만 필수 검증을 대체하지 않는 smoke/helper 검증> + +## 판정 기준 + +- <통과로 판단할 출력, 상태, UX, artifact, endpoint 조건> + +## 기준 출력 예시 + +```text +<필요한 경우 기대 출력 예시> +``` + +## 차단 기준 + +- <테스트를 계속할 수 없는 환경/권한/도구/서비스 조건> + +## 보고 항목 + +- 실행한 명령: +- 성공한 검증: +- 실패/차단된 검증: +- 생략 사유: +- 남은 위험: + +## 금지 사항 + +- secret, token, 개인 endpoint 원문은 tracked 파일에 기록하지 않는다. + diff --git a/agent-test/dev/edge-smoke.md b/agent-test/dev/edge-smoke.md index c790e5d..14b13d7 100644 --- a/agent-test/dev/edge-smoke.md +++ b/agent-test/dev/edge-smoke.md @@ -3,7 +3,7 @@ test_env: dev test_profile: edge-smoke domain: edge verification_type: smoke -last_rule_updated_at: 2026-06-12 +last_rule_updated_at: 2026-06-24 --- # edge-smoke dev 테스트 @@ -41,6 +41,40 @@ last_rule_updated_at: 2026-06-12 - 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 node: `mac-codex-node` / `mac-codex` + - SSH/user: `ssh toki@toki-labs.com` + - 목적: `cli` adapter, `codex app-server` + - workspace: `/Users/toki/agent-work/iop-workspace/nomadcode` +- 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` + - 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` + - workspace: `C:/Users/r0bin/iop-field` + +OneXPlayer Lemonade Node는 원격 runner나 Edge host에서 다시 SSH하거나 proxy process로 띄우지 않는다. 현재 작업 호스트에서 OneXPlayer Windows host에 `ssh r0bin@192.168.0.59`로 직접 접속한 뒤 generated PowerShell bootstrap을 실행한다. + ## 명령 - setup: @@ -69,6 +103,9 @@ last_rule_updated_at: 2026-06-12 - node 로컬 `[node-message]` payload 라인 목록과 edge `[node-*-message]` payload 라인 목록이 run별로 내용/순서까지 동일해야 한다. - edge complete는 같은 run의 마지막 `[node-*-message]` 이후에만 정상이다. - OpenAI-compatible smoke에서 `/healthz`, `/v1/models`, `/v1/responses`가 기대 상태로 응답한다. +- 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`이면 endpoint별 8개 동시 요청에서 총 `in_flight=7`, `queued>=1` 관측을 기준으로 한다. +- capacity smoke 완료 후 대상 provider의 `in_flight=0`, `queued=0` 회복을 확인한다. +- 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 하나만 포함한다. ## 기준 출력 예시 diff --git a/agent-test/dev/inventory.yaml b/agent-test/dev/inventory.yaml new file mode 100644 index 0000000..3cea1ea --- /dev/null +++ b/agent-test/dev/inventory.yaml @@ -0,0 +1,80 @@ +test_env: dev +profile: dev-runtime-provider-pool +last_updated_at: "2026-06-24" + +source: + remote_runner: + ssh: toki@toki-labs.com + repo_root: /Users/toki/agent-work/iop-dev + clean_sync: + - git fetch origin main + - git reset --hard origin/main + - git clean -fd + dirty_policy: discard + +edge: + id: edge-toki-labs-dev + config_path: build/dev-runtime/edge.yaml + control_plane_http: http://127.0.0.1:18001 + control_plane_status_url: http://127.0.0.1:18001/edges/edge-toki-labs-dev/status + bootstrap_http_public: http://toki-labs.com:18082 + openai_base_url_public: http://toki-labs.com:18083/v1 + openai_base_url_runner: http://127.0.0.1:18083/v1 + edge_node_tcp_public: toki-labs.com:18084 + admin_addr_runner: 127.0.0.1:19093 + +build: + binaries: + edge: build/dev-runtime/bin/edge + node_macos: build/dev-runtime/bin/iop-node + node_linux_arm64: build/dev-runtime/bin/iop-node-linux-arm64 + node_windows_amd64: build/dev-runtime/bin/iop-node-windows-amd64.exe + +model: + alias: qwen3.6:35b + provider_capacity_total: 7 + capacity_smoke: + endpoints: + - /v1/responses + - /v1/chat/completions + concurrent_requests: capacity_plus_one + expected_total_in_flight: 7 + expected_min_queued: 1 + prompt_policy: long_reasoning_allowed + exact_output_match: false + +nodes: + - id: mac-codex-node + alias: mac-codex + role: cli + ssh: toki@toki-labs.com + workspace: /Users/toki/agent-work/iop-workspace/nomadcode + provider_pool_candidate: false + adapters: + - cli + - id: gx10-vllm-node + alias: gx10-vllm + role: vllm-provider + ssh: toki@192.168.0.91 + workspace: /home/toki/iop-gx10-vllm + provider_pool_candidate: true + provider: + id: gx10-vllm + type: vllm + endpoint: http://192.168.0.91:8001/v1 + served_model: nvidia/Qwen3.6-35B-A3B-NVFP4 + capacity: 4 + - id: onexplayer-lemonade-node + alias: onexplayer-lemonade + role: lemonade-provider + ssh: r0bin@192.168.0.59 + ssh_origin: current_host + workspace: C:/Users/r0bin/iop-field + provider_pool_candidate: true + provider: + id: onexplayer-lemonade + type: lemonade + endpoint: http://192.168.0.59:13305/v1 + served_model: Qwen3.6-35B-A3B-MTP-GGUF + capacity: 3 + windows_process_start: Win32_Process.Create diff --git a/agent-test/dev/node-smoke.md b/agent-test/dev/node-smoke.md index b44f33a..988e45a 100644 --- a/agent-test/dev/node-smoke.md +++ b/agent-test/dev/node-smoke.md @@ -3,7 +3,7 @@ test_env: dev test_profile: node-smoke domain: node verification_type: smoke -last_rule_updated_at: 2026-06-12 +last_rule_updated_at: 2026-06-24 --- # node-smoke dev 테스트 @@ -40,6 +40,30 @@ last_rule_updated_at: 2026-06-12 - model endpoint: dev OpenAI-compatible base URL 후보 `http://toki-labs.com:18083/v1` - credential: token/secret 원문은 문서에 기록하지 않는다. +## dev-runtime Node 접속 기준 + +dev-runtime의 실제 3-node 연결을 점검할 때는 원격 runner `ssh toki@toki-labs.com`의 `/Users/toki/agent-work/iop-dev` checkout과 `build/dev-runtime/edge.yaml`을 기준으로 한다. Node는 dev-runtime provider pool 기준에서 Edge-Node TCP `toki-labs.com:18084`로 붙는다. + +- mac CLI node: `mac-codex-node` / `mac-codex` + - SSH/user: `ssh toki@toki-labs.com` + - 목적: `cli` adapter, `codex app-server` + - workspace: `/Users/toki/agent-work/iop-workspace/nomadcode` +- 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` + - 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` + - workspace: `C:/Users/r0bin/iop-field` + +GX10은 Linux/ARM64 bootstrap, OneXPlayer는 Windows native PowerShell bootstrap을 기본으로 한다. OneXPlayer는 현재 작업 호스트에서 직접 접속해 세팅하며, 원격 runner나 Edge host에서 `node-onexplayer-lemonade.yaml`로 proxy 실행하지 않는다. + +OneXPlayer에서 SSH 세션 안의 `Start-Process`로 `iop-node.exe`를 띄우면 SSH 세션 종료와 함께 process가 정리될 수 있다. dev 반복 배포에서는 `Win32_Process.Create` 또는 동등한 세션 독립 실행 방식으로 `C:/Users/r0bin/iop-field`에서 `iop-node.exe --config node.yaml serve`를 시작하고, WMI/process query와 `iop-node.log`의 `connected to edge` 로그로 유지 여부를 확인한다. + ## 명령 - setup: diff --git a/agent-test/dev/rules.md b/agent-test/dev/rules.md index 6729c99..fee28a3 100644 --- a/agent-test/dev/rules.md +++ b/agent-test/dev/rules.md @@ -1,6 +1,6 @@ --- test_env: dev -last_rule_updated_at: 2026-06-12 +last_rule_updated_at: 2026-06-24 --- # dev 테스트 규칙 @@ -13,6 +13,7 @@ last_rule_updated_at: 2026-06-12 - dev 테스트/검증은 이 파일을 기준으로 판단한다. - 작업 완료 검증은 변경 범위 기준으로 선택한다. - 필수 검증을 실행하지 못하면 차단 사유로 보고한다. +- 새 테스트 환경을 추가하거나 환경 구조를 바꿀 때는 `agent-test/README.md`와 `agent-test/_templates/`의 확장 기준을 따른다. - 최종 보고에는 실행 명령, 결과, 생략 사유, 남은 위험을 남긴다. - 환경값, secret, 개인 endpoint는 tracked docs/roadmap에 쓰지 않는다. - token/secret 원문은 원격 환경에서 주입하고, shell stdout/stderr와 최종 보고에 출력하지 않는다. @@ -23,7 +24,7 @@ last_rule_updated_at: 2026-06-12 - host: dev runtime evidence는 원격 runner `ssh toki@toki-labs.com`의 `/Users/toki/agent-work/iop-dev` 기준으로 수행한다. - repo root: 명령은 원격 checkout `/Users/toki/agent-work/iop-dev` 기준으로 실행한다. -- sync 기준: 원격 runner를 사용하는 경우 local uncommitted 변경은 원격 repo에 자동 반영되지 않는다. 동기화되지 않았으면 현재 변경분 검증으로 보지 않는다. +- sync 기준: dev 배포 전 원격 runner checkout은 `git fetch origin main`, `git reset --hard origin/main`, `git clean -fd`로 clean 상태를 만든 뒤 빌드한다. dev runner의 dirty 변경은 보존 대상으로 보지 않는다. - env file: dev stack은 `docker compose --env-file .env.dev.example ...`로 명시한다. - compose identity: `COMPOSE_PROJECT_NAME=iop-dev-agent`, `IOP_COMPOSE_NETWORK=iop-dev-agent-net`. - port: web/dev preview `13001`, Control Plane HTTP `18001`, Portal/Control Plane wire test endpoint `19001`, CP-Edge wire `19002`, Edge-Node TCP transport `19003`, Postgres host publish `15401`, Redis host publish `16301`. @@ -50,10 +51,47 @@ last_rule_updated_at: 2026-06-12 | PostgreSQL host publish | `15400` | `15401` | | Redis host publish | `16300` | `16301` | +dev-runtime provider pool은 compose Edge-Node TCP `19003`이 아니라 native Edge listen `18084`를 사용한다. 세부 기준은 `agent-test/dev/edge-smoke.md`와 `agent-test/dev/node-smoke.md`를 따른다. + +## 런타임 프로필 + +- local quick check: 현재 checkout에서 Go quick check를 우선 실행한다. +- remote runner: dev runtime evidence, Flutter client, Docker compose, field/bootstrap, 외부 runtime evidence는 `ssh toki@toki-labs.com`의 `/Users/toki/agent-work/iop-dev` 기준으로 수행한다. +- compose dev stack: `.env.dev.example`, `COMPOSE_PROJECT_NAME=iop-dev-agent`, `IOP_COMPOSE_NETWORK=iop-dev-agent-net`, Edge-Node TCP `19003`을 사용한다. +- Edge direct dev profile: artifact/bootstrap `18082`, OpenAI-compatible `18083`, metrics `19101`을 필요할 때만 사용한다. +- dev-runtime provider pool: `/Users/toki/agent-work/iop-dev/build/dev-runtime/edge.yaml`과 Edge-Node TCP `toki-labs.com:18084`를 사용한다. 3-node/provider 세부는 `agent-test/dev/edge-smoke.md`와 `agent-test/dev/node-smoke.md`를 따른다. +- external provider field: GX10 vLLM은 `ssh toki@192.168.0.91`, OneXPlayer Lemonade는 현재 작업 호스트에서 `ssh r0bin@192.168.0.59`로 직접 접속해 확인한다. OneXPlayer 접속은 원격 runner 경유를 필수 조건으로 보지 않는다. + +## 프리플라이트 + +- plan 작성 시 필수 검증이 현재 checkout을 벗어나 원격 runner, field/bootstrap, 외부 provider, Docker/code-server, emulator/device, 공유 장기 runtime을 사용하면 먼저 테스트 환경 프리플라이트를 계획에 기록한다. +- 테스트 환경 프리플라이트에는 runner, repo root/workdir, branch/HEAD/dirty 상태, local 변경과 원격 source 동기화 여부, binary/artifact 경로와 필요한 help/version 출력, config path, runtime identity, port/process 상태, 외부 host, OS/arch 가정을 포함한다. +- 프리플라이트에서 dirty/divergent checkout 또는 stale artifact가 확인되면 dev 배포에서는 먼저 원격 checkout을 clean sync하고 dev-runtime 바이너리를 rebuild한다. 잘못된 identity, missing command, closed port, host OS 불일치, source 미동기화가 확인되면 plan은 setup/sync/rebuild 단계를 만들거나 blocker로 보고한다. profile 값을 이미 참이라고 가정한 검증 명령만 쓰지 않는다. + +## 노드/Provider 인벤토리 위치 + +- dev-runtime provider pool machine-readable 기준: `agent-test/dev/inventory.yaml` +- dev-runtime provider pool과 Edge/OpenAI-compatible 입력 표면 상세 기준: `agent-test/dev/edge-smoke.md` +- dev-runtime Node 접속과 bootstrap 상세 기준: `agent-test/dev/node-smoke.md` +- 공통 provider config 계약 기준: `agent-test/dev/platform-common-smoke.md` +- compose dev stack 기준: `agent-test/dev/control-plane-smoke.md`, `agent-test/dev/client-smoke.md`, `agent-test/dev/testing-smoke.md` + +## Field/bootstrap 반복 테스트 기준 + +- field/bootstrap 검증은 원격 runner의 `/Users/toki/agent-work/iop-dev` checkout과 dev-runtime artifact를 기준으로 수행한다. 세부 Edge/Node profile은 `agent-test/dev/edge-smoke.md`와 `agent-test/dev/node-smoke.md`를 따른다. +- compose dev stack은 Edge-Node TCP `19003`을 사용하고, dev-runtime provider pool은 native Edge listen `18084`를 사용한다. 두 프로필을 섞어서 판정하지 않는다. +- Node bootstrap은 Edge의 `node register`가 출력한 OS별 완성 명령을 그대로 사용한다. 사용자에게 안내하는 명령에는 임의 placeholder, 수동 token 치환, `IOP_*=` named environment parameter를 넣지 않는다. +- token, API key, private credential 원문은 tracked docs, roadmap, 테스트 규칙에 기록하지 않는다. 실행 증거에는 token을 마스킹하거나 명령 생성 사실만 남긴다. +- Linux/macOS Node는 생성된 `curl | bash` 계열 명령을 사용한다. Windows Node는 native PowerShell bootstrap을 기본으로 사용한다. +- dev-runtime 배포는 clean sync 후 `build/dev-runtime/bin/edge`, mac node, Linux/Windows node binary를 같은 source 기준으로 rebuild한다. 배포 후 `edge config refresh --help`, `19093` port, `config refresh --mode dry-run`을 확인한다. +- provider capacity, model/provider mapping은 `config check`와 `config refresh --mode dry-run`을 통과한 뒤 `config refresh --mode apply`로 반영한다. refresh subcommand 또는 `19093` admin port가 없으면 바이너리 rebuild 누락으로 보고 먼저 rebuild한다. +- Edge process restart 또는 일시 단절 후에는 Node reconnect 정책을 검증한다. retry 한계를 넘겨 Node process가 종료된 경우에만 해당 Node host에서 새 bootstrap 실행이 필요하다. + ## 라우팅 - node / smoke / node 실행 파이프라인 baseline: `agent-test/dev/node-smoke.md` - edge / smoke / edge 실행 그룹과 입력 표면 baseline: `agent-test/dev/edge-smoke.md` +- dev-runtime provider pool, 3-node 연결, GX10 vLLM, OneXPlayer Lemonade, mac CLI node 점검: `agent-test/dev/edge-smoke.md`, `agent-test/dev/node-smoke.md` - control-plane / smoke / control-plane health와 wire baseline: `agent-test/dev/control-plane-smoke.md` - client / smoke / Flutter client와 IOP console package baseline: `agent-test/dev/client-smoke.md` - platform-common / smoke / 공통 설정과 protobuf 계약 baseline: `agent-test/dev/platform-common-smoke.md` diff --git a/apps/node/internal/adapters/cli/codex_app_server_internal_test.go b/apps/node/internal/adapters/cli/codex_app_server_internal_test.go index a0ae601..ea7058e 100644 --- a/apps/node/internal/adapters/cli/codex_app_server_internal_test.go +++ b/apps/node/internal/adapters/cli/codex_app_server_internal_test.go @@ -1077,13 +1077,8 @@ echo '{"id":1,"result":{"protocolVersion":"2024-11-05","capabilities":{}}}' } defer proc.close() - time.Sleep(150 * time.Millisecond) - markerPath := filepath.Join(workspace, "cwd.txt") - cwdBytes, err := os.ReadFile(markerPath) - if err != nil { - t.Fatalf("failed to read marker file: %v", err) - } + cwdBytes := readFileEventually(t, markerPath, 2*time.Second) cwd := strings.TrimSpace(string(cwdBytes)) resolvedWorkspace, err := os.Readlink(workspace) @@ -1095,3 +1090,21 @@ echo '{"id":1,"result":{"protocolVersion":"2024-11-05","capabilities":{}}}' t.Errorf("expected process cwd to be %q or %q, got %q", workspace, resolvedWorkspace, cwd) } } + +func readFileEventually(t *testing.T, path string, timeout time.Duration) []byte { + t.Helper() + + deadline := time.Now().Add(timeout) + var lastErr error + for { + b, err := os.ReadFile(path) + if err == nil { + return b + } + lastErr = err + if time.Now().After(deadline) { + t.Fatalf("failed to read file %s within %s: %v", path, timeout, lastErr) + } + time.Sleep(10 * time.Millisecond) + } +} diff --git a/apps/node/internal/adapters/cli/oneshot.go b/apps/node/internal/adapters/cli/oneshot.go index ce4a3bd..acd4d26 100644 --- a/apps/node/internal/adapters/cli/oneshot.go +++ b/apps/node/internal/adapters/cli/oneshot.go @@ -16,7 +16,10 @@ import ( func (c *CLI) executeOneShot(ctx context.Context, spec runtime.ExecutionSpec, profile config.CLIProfileConf, sink runtime.EventSink) error { prompt := extractPrompt(spec.Input) - args := append(append([]string{}, profile.Args...), prompt) + args := append([]string{}, profile.Args...) + if prompt != "" { + args = append(args, prompt) + } _, err := c.executeCommand(ctx, spec, profile, args, prompt, sink) return err } diff --git a/docs/edge-local-dev-guide.md b/docs/edge-local-dev-guide.md index 5913cf9..8a72028 100644 --- a/docs/edge-local-dev-guide.md +++ b/docs/edge-local-dev-guide.md @@ -103,6 +103,20 @@ curl -fsS http://toki-labs.com:18000/edges/edge-toki-labs/status 현재 GX10 vLLM과 OneXPlayer Lemonade를 같은 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` @@ -118,9 +132,17 @@ curl -fsS http://toki-labs.com:18000/edges/edge-toki-labs/status - provider candidate: `onexplayer-lemonade` on `onexplayer-lemonade-node`, capacity `3` - connected non-candidate: `mac-codex-node` -OneXPlayer Lemonade Node는 원격 runner나 Edge host에서 proxy process로 띄우지 않는다. Windows host에 직접 SSH로 접속한 뒤, 그 host 안에서 generated PowerShell bootstrap 명령을 실행한다. 이때 Lemonade provider endpoint는 Windows host 로컬에서 접근 가능한 값을 기준으로 검증한다. +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 로컬에서 접근 가능한 값을 기준으로 검증한다. -capacity, provider mapping, model alias 변경은 실행 중 Edge에 `config refresh`로 반영한다. 후보 `edge.yaml` 수정 후 `config check`, dry-run, apply 순서로 진행한다. listen port, Edge identity, bootstrap artifact dir처럼 live apply가 위험한 값은 `restart_required`로 보고되며 Edge process 재시작이 필요하다. +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`으로 잡고 실행한다. @@ -140,12 +162,17 @@ Node host OS 재부팅은 필요하지 않다. Edge process 재시작이나 일 3. Edge process를 재시작하고 bootstrap 재실행 없이 3개 Node 연결이 회복되는지 확인한다. 4. `http://toki-labs.com:18083/healthz`와 `/v1/models`를 확인한다. 5. capacity를 `gx10-vllm=4`, `onexplayer-lemonade=3`로 맞춘 후보 config를 refresh apply하고 Edge process가 유지되는지 확인한다. -6. `qwen3.6:35b`로 동시 4개 `/v1/chat/completions` 요청을 보낸다. -7. capacity `4/3` 기준이면 동시 in-flight 전제에서 aggregate 배정 기대값은 `gx10-vllm` 2개, `onexplayer-lemonade` 2개다. +6. `qwen3.6:35b`로 `/v1/responses`와 `/v1/chat/completions` 각각에 provider capacity 총합 + 1개 동시 요청을 보낸다. +7. capacity `gx10-vllm=4`, `onexplayer-lemonade=3` 기준이면 endpoint별 8개 동시 요청에서 Control Plane status의 provider snapshot이 총 `in_flight=7`, `queued>=1`을 한 번 이상 보여야 한다. +8. 요청 완료 후 provider snapshot이 `in_flight=0`, `queued=0`으로 회복되는지 확인한다. + +Qwen 계열 모델은 thinking/reasoning 텍스트를 포함해 응답할 수 있다. 이 dev smoke에서는 thinking 출력을 실패로 보지 않고, HTTP 성공, final marker 포함, provider log/run count 증가를 기준으로 판정한다. 현재 공개 API는 개별 request의 최종 `node_id`를 응답에 노출하지 않는다. 요청별 배정을 확정해야 할 때는 Edge dispatch trace/log를 추가한 뒤 판정한다. -## 7. Smoke +## 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