update: sync contract, test documents and cleanup task
This commit is contained in:
parent
59e55d409e
commit
53e09201a0
7 changed files with 301 additions and 139 deletions
|
|
@ -224,11 +224,11 @@ Strict output 모드:
|
|||
`tools`가 있는 Chat Completions 요청에서 provider route(`openai_compat`, `vllm`, `ollama`, provider pool)는 forced tool 선택 객체와 `"none"` 같은 명시적 `tool_choice`를 backend에 전달한다. 단, `"auto"`는 OpenAI-compatible 기본값과 같으므로 provider request에서는 생략한다. 일부 vLLM 계열 backend는 explicit/default `"auto"`를 `--enable-auto-tool-choice`/`--tool-call-parser` 없이 400으로 거부한다. 이 400이 발생하고 요청 tool이 정확히 1개이면 Node adapter는 해당 tool에 대한 forced `tool_choice`로 1회 재시도한다. forced tool도 `--tool-call-parser` 요구로 거부되거나 여러 tool이라 forced를 고를 수 없으면, Node adapter는 `tools`/`tool_choice`를 제거하고 text tool-call system instruction을 leading system message에 병합해 1회 재시도하며 완료 metadata에 `openai_text_tool_fallback: "true"`를 싣는다.
|
||||
provider가 native OpenAI-compatible `tool_calls`를 반환하면 Node는 내부 `RunEvent.metadata["openai_tool_calls"]` JSON으로 보존하고, Edge는 이를 OpenAI-compatible `message.tool_calls` 또는 stream `delta.tool_calls`로 반환하며 `finish_reason: "tool_calls"`를 사용한다.
|
||||
provider native `tool_calls[].function.arguments`는 OpenAI 계약에 맞는 JSON string으로 반환한다. 단, provider가 요청 `tools[].function.parameters` schema상 배열/객체여야 하는 값을 JSON 문자열로 이중 인코딩한 경우 Edge는 해당 `arguments` JSON만 schema 기준으로 복원해 다시 JSON string으로 직렬화한다.
|
||||
provider route에서 assistant content에 `<tool_call>`, `{{function(...)}}`, `[Calling tool: ...]` 같은 텍스트가 들어와도 Edge는 기본적으로 이를 파싱하거나 OpenAI `tool_calls`로 합성하지 않는다. 해당 텍스트는 backend가 반환한 content로 그대로 둔다. 예외적으로 Node adapter가 native tool API unsupported fallback을 수행해 `openai_text_tool_fallback: "true"` metadata를 붙인 실행에 한해서만 Edge가 text tool-call을 OpenAI `tool_calls`로 복원한다.
|
||||
CLI route(`adapter: "cli"`)는 native backend tool calling이 없으므로, 호환 fallback으로만 assistant content의 Cline-style 텍스트 tool call 블록을 OpenAI-compatible `tool_calls`로 합성할 수 있다. 이 fallback이 지원하는 텍스트 블록의 최소 형태는 `<tool_call><function=<name>><parameter=<key>>JSON-or-text</parameter></function></tool_call>` 또는 `{{function_name(key=Python/JSON-like-literal)}}`이다.
|
||||
CLI fallback에서 텍스트 tool call을 구조화할 때만 Edge는 요청의 `tools[].function.parameters` schema를 기준으로 arguments를 정규화한다. 예를 들어 tool schema가 `commands: string[]`만 허용하면 command 객체 입력도 shell string 배열로 접고, `commands: {command,args}[]`를 허용하면 shell 문법이 없는 명령을 structured argv로 만든다.
|
||||
CLI fallback에서 schema에 없는 UI 설명용 `description`이나 실행 위치 힌트용 `runInTerminal`은 command 객체와 최상위 args에서 제거한다. 단, `cd`, `command -v`, `&&`, pipe, redirect, quote 등 shell 해석이 필요한 명령은 schema가 허용할 때 `commands: ["cd /work && git status"]` 같은 shell string으로 유지한다.
|
||||
CLI route에서는 backend auto tool-calling 요구 조건으로 요청이 실패하지 않도록 내부 실행 입력의 `tool_choice`를 `"none"`으로 낮춘다.
|
||||
요청에 `tools[]`가 있고 provider가 native `tool_calls` 없이 assistant content에 raw text tool-call 블록을 담아 응답하면, provider route(`openai_compat`, `vllm`, `ollama`, provider pool)와 CLI route(`adapter: "cli"`) 모두에서 Edge는 그 블록을 요청 tool schema 기준으로 구조화하거나 차단한다. 이 정규화가 인식하는 텍스트 블록의 최소 형태는 `<tool_call><function=<name>><parameter=<key>>JSON-or-text</parameter></function></tool_call>` XML 형식과 `{{function_name(key=Python/JSON-like-literal)}}` mustache 형식이다.
|
||||
후보 tool 이름이 요청 `tools[]`에 있고 arguments가 파싱되어 해당 tool의 `function.parameters` schema를 만족하면, Edge는 이를 OpenAI `tool_calls`로 정규화하고 raw 블록을 `content`에서 제거한 뒤 `finish_reason: "tool_calls"`로 반환한다. 요청 `tools[]`에 없는 tool 이름, unclosed/function 정의 누락 같은 malformed 블록, schema를 위반하는 arguments는 성공 content로 반환하지 않고 tool validation 실패로 처리한다. non-stream과 strict buffered stream 응답은 bounded tool-validation attempt 한도까지 run을 재시도하고, 그래도 실패하면 `tool_validation_error`로 응답한다. live SSE 스트림은 raw 블록을 content delta로 flush하지 않고 `tool_validation_error` 이벤트로 스트림을 종료한다.
|
||||
요청에 `tools[]`가 없으면 assistant content의 tool-call 유사 텍스트는 파싱하거나 합성하지 않고 backend content 원문으로 그대로 둔다. 자연어 추론은 어떤 경우에도 `tool_calls`로 변환하지 않는다.
|
||||
raw `<tool_call>`/`{{...}}` 블록과 `<|mask_end|>` 같은 알려진 chat-template sentinel은 성공 응답의 `content`나 SSE delta에 노출하지 않는다. sentinel은 content와 reasoning 양쪽에서, streaming chunk 경계에 걸쳐 분할되더라도 sanitize한다.
|
||||
text tool-call을 구조화할 때 Edge는 route와 무관하게 요청의 `tools[].function.parameters` schema를 기준으로 arguments를 정규화한다. 예를 들어 tool schema가 `commands: string[]`만 허용하면 command 객체 입력도 shell string 배열로 접고, `commands: {command,args}[]`를 허용하면 shell 문법이 없는 명령을 structured argv로 만든다. schema에 없는 UI 설명용 `description`이나 실행 위치 힌트용 `runInTerminal`은 command 객체와 최상위 args에서 제거하되, `cd`, `command -v`, `&&`, pipe, redirect, quote 등 shell 해석이 필요한 명령은 schema가 허용할 때 `commands: ["cd /work && git status"]` 같은 shell string으로 유지한다. CLI route(`adapter: "cli"`)는 native backend tool calling이 없어 이 text tool-call 구조화가 유일한 `tool_calls` 경로이며, backend auto tool-calling 요구 조건으로 요청이 실패하지 않도록 내부 실행 입력의 `tool_choice`를 `"none"`으로 낮춘다.
|
||||
`parallel_tool_calls`, `stream_options`, `store`는 클라이언트 호환성을 위해 수신하지만 현재 Edge 실행 의미에는 반영하지 않는다.
|
||||
|
||||
금지:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,210 @@
|
|||
<!-- task=m-openai-compatible-tool-call-boundary-hardening/02+01_contract_dev_smoke plan=0 tag=API -->
|
||||
|
||||
# Code Review Reference - API
|
||||
|
||||
> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
|
||||
> The task is NOT complete until every implementation-owned section below is filled in.
|
||||
> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving.
|
||||
> Fill implementation-owned sections, then stop with active files in place and report ready for review.
|
||||
> If implementation is blocked by a selected Milestone `구현 잠금 > 결정 필요` item, fill `사용자 리뷰 요청` with linked evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`.
|
||||
> Do not ask the user directly, present choices in chat, or call `request_user_input` during implementation.
|
||||
> Finalization is review-agent-only.
|
||||
|
||||
## 개요
|
||||
|
||||
date=2026-07-04
|
||||
task=m-openai-compatible-tool-call-boundary-hardening/02+01_contract_dev_smoke, plan=0, tag=API
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/openai-compatible-tool-call-boundary-hardening.md`
|
||||
- Task ids:
|
||||
- `client-smoke`: Pi/Cline형 긴 agent prompt와 `tools[]` dev Edge 호출 evidence
|
||||
- `contract-docs`: OpenAI-compatible 계약 문서와 dev 운영 문서 정책 설명
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 이 파일을 읽는 리뷰 에이전트에게
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.
|
||||
|
||||
각 항목의 구현을 실제 파일과 evidence path에 대조하고, secret 원문이 tracked 파일이나 review output에 남지 않았는지 확인하세요.
|
||||
|
||||
1. 판정을 append한다.
|
||||
2. `CODE_REVIEW-cloud-G07.md`와 `PLAN-cloud-G07.md`를 `.log`로 아카이브한다.
|
||||
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 archive로 이동한다.
|
||||
4. PASS이고 task group이 `m-openai-compatible-tool-call-boundary-hardening`이면 완료 이벤트 메타데이터를 보고한다. roadmap 수정은 런타임 책임이다.
|
||||
|
||||
---
|
||||
|
||||
## 구현 항목별 완료 여부
|
||||
|
||||
| 항목 | 완료 여부 |
|
||||
|------|---------|
|
||||
| [API-1] Contract Policy Update | [x] |
|
||||
| [API-2] Dev Smoke Guide And Test Rule | [x] |
|
||||
| [API-3] Credentialed Dev Runtime Evidence | [x] |
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [x] 01번 `agent-task/m-openai-compatible-tool-call-boundary-hardening/01_provider_text_boundary/complete.log`가 존재하고 Roadmap Completion에 `provider-raw-parse`, `unknown-tool-error`, `stream-flush-guard`, `native-preserve`가 포함됐는지 확인한다. (archive `agent-task/archive/2026/07/m-openai-compatible-tool-call-boundary-hardening/01_provider_text_boundary/complete.log`에서 PASS와 4개 task id 확인)
|
||||
- [x] `agent-contract/outer/openai-compatible-api.md`가 provider route/provider-pool raw text tool-call 후보의 구조화/차단, unknown/malformed 처리, sentinel sanitize 정책을 설명하도록 갱신한다.
|
||||
- [x] `docs/edge-local-dev-guide.md`와 `agent-test/dev/edge-smoke.md`에 dev-runtime Pi/Cline형 `tools[]` smoke 기준, evidence 보관 위치, secret 마스킹 원칙을 추가한다.
|
||||
- [x] 원격 dev-runtime source를 01 완료 source와 동기화하고 dev-runtime Edge/Node binaries를 rebuild한 뒤 `config check`와 필요한 refresh/restart를 수행한다. (01 변경은 `apps/edge/`에 국한, `apps/node/` 무변경이라 Edge 바이너리만 rebuild + Edge serve 재시작; 3개 node는 reconnect 정책으로 유지)
|
||||
- [x] dev Edge OpenAI-compatible endpoint에 긴 agent prompt와 `tools[]` 요청을 호출해 valid raw text는 `tool_calls`, unknown hallucination은 `tool_validation_error` 또는 retry 후 차단으로 끝나며 raw block이 body/SSE에 남지 않는 evidence를 남긴다.
|
||||
- [x] `go test ./apps/edge/internal/openai -count=1`와 문서/evidence 검증 명령을 실행한다.
|
||||
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 코드리뷰 전용 체크리스트
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
|
||||
|
||||
- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
|
||||
- [x] active `CODE_REVIEW-*-G??.md`를 `code_review_cloud_G07_N.log`로 아카이브한다.
|
||||
- [x] active `PLAN-*-G??.md`를 `plan_cloud_G07_M.log`로 아카이브한다.
|
||||
- [x] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다.
|
||||
- [x] PASS이면 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
|
||||
- [x] PASS이면 active task 디렉터리를 `agent-task/archive/YYYY/MM/m-openai-compatible-tool-call-boundary-hardening/02+01_contract_dev_smoke/`로 이동한다.
|
||||
- [x] PASS이고 task group이 `m-openai-compatible-tool-call-boundary-hardening`이면 런타임 완료 이벤트 메타데이터를 보고하고 roadmap을 직접 수정하지 않는다.
|
||||
- [x] PASS split 작업이므로 이동 후 빈 active parent `agent-task/m-openai-compatible-tool-call-boundary-hardening/`를 제거했다.
|
||||
- [x] WARN/FAIL이 아니므로 다음 active plan/review 파일 또는 unresolved verification report를 작성하지 않았다.
|
||||
|
||||
## 계획 대비 변경 사항
|
||||
|
||||
- 계약 문단(API-1)을 계획의 "provider route/provider-pool" 한정 서술보다 넓게 갱신했다. 실제 post-01 코드에서 `collectChatCompletionOutput`/live·buffered stream은 `len(req.Tools) > 0`이면 route 구분 없이 text tool-call을 synthesize/validate하며, `shouldSynthesizeTextToolCalls`(fallback/cli 게이트)는 정의만 있고 호출되지 않는 dead symbol이다. 따라서 계약을 "provider route + CLI route 모두"로 서술하고, 구 계약의 `openai_text_tool_fallback` 예외 조건 서술은 제거했다. `tools[]`가 없으면 합성/파싱하지 않는다는 제외 조건은 유지했다.
|
||||
- 원격 rebuild 범위를 Edge 바이너리로 한정했다. `git diff --stat b55d1a2..59e55d4 -- apps/`가 01 boundary 변경이 전부 `apps/edge/`(openai 패키지 + `node/mapper.go`)이고 `apps/node/` 무변경임을 보여, 계획의 "Edge/Node binaries rebuild" 중 Node 재빌드/재배포는 불필요로 판단했다. Edge serve만 새 바이너리로 재시작했고 3개 node는 reconnect 정책으로 유지되어 재-bootstrap 없이 connected를 확인했다.
|
||||
- unknown-tool 라이브 evidence는 2회 시도했다. 1차(자유 유도)에서는 모델이 `<tool_call>`/`{{}}` 구조 블록이 아닌 산문(`lookup_weather(city="Seoul")`)을 내 content로 반환(leak 없음, finish_reason stop)되어 validation-error 경로를 유도하지 못했다. 2차에서 literal `<tool_call>` unknown-tool 블록을 강제 출력하도록 프롬프트해 실제 `tool_validation_error`(`tool "lookup_weather" is not in request tools`) 차단을 라이브로 확인했다.
|
||||
|
||||
## 주요 설계 결정
|
||||
|
||||
- `docs/openai-compatible-api-contract.md`는 계약 pointer이므로 원문 정책을 복제하지 않고 그대로 두었다(grep으로 raw/tool_validation 문구 0건 확인). source of truth는 `agent-contract/outer/openai-compatible-api.md`.
|
||||
- credential은 원격 `edge.yaml`의 `bearer_token`을 원격 셸 변수로만 추출해 `Authorization` 헤더에 인라인 주입했고, 명령/로그/evidence 파일/보고 어디에도 원문을 남기지 않았다(evidence dir 대상 token grep 0건).
|
||||
- dev evidence는 gitignore된 `build/dev-runtime/logs/boundary-smoke-<ts>/`(원격)에 request/response 원문을 저장하고, tracked 문서에는 마스킹 요약과 경로만 남겼다.
|
||||
- 결정적 차단 커버리지(unknown/malformed retry·fail, stream block, sentinel drop, every-candidate validation)는 01의 로컬 unit test 22종이 담당하고, 라이브 dev smoke는 실제 provider-pool 경로에서 no-raw-leak + valid 정규화 + unknown 차단을 보완 검증하는 역할로 분리했다.
|
||||
|
||||
## 사용자 리뷰 요청
|
||||
|
||||
_기본값은 `없음`이다. 구현 중 새 결정이 필요해 보여도 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 이 섹션은 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 차단할 때만 채운다. 외부 환경/secret/서비스 준비, 검증 증거 공백, 반복 실패, 일반 범위 조정은 사용자 리뷰 요청이 아니며 `검증 결과`, `계획 대비 변경 사항`, 또는 code-review의 일반 follow-up plan으로 처리한다._
|
||||
|
||||
- 상태: 없음
|
||||
- 사유 유형: 없음
|
||||
- 연결 대상: 없음
|
||||
- 결정 필요: 없음
|
||||
- 차단 근거: 없음
|
||||
- 실행한 검증/명령: 없음
|
||||
- 자동 후속 불가 이유: 없음
|
||||
- 재개 조건: 없음
|
||||
|
||||
## 리뷰어를 위한 체크포인트
|
||||
|
||||
- 01 predecessor completion이 실제로 충족됐는지 확인한다.
|
||||
- 계약 문서가 `docs/`에 중복 원문을 만들지 않고 `agent-contract`를 source of truth로 유지하는지 확인한다.
|
||||
- dev smoke evidence가 token/secret 원문을 노출하지 않는지 확인한다.
|
||||
- raw `<tool_call>`, unknown tool hallucination, `<|mask_end|>` leak 차단 evidence가 남았는지 확인한다.
|
||||
|
||||
## 검증 결과
|
||||
|
||||
_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._
|
||||
|
||||
### API-1 중간 검증
|
||||
```bash
|
||||
$ rg --sort path -n "provider route에서|raw text|tool_validation_error|mask_end" agent-contract/outer/openai-compatible-api.md docs/openai-compatible-api-contract.md
|
||||
agent-contract/outer/openai-compatible-api.md:227: ... raw text tool-call 블록을 ... provider route ... CLI route ... 구조화하거나 차단한다.
|
||||
agent-contract/outer/openai-compatible-api.md:228: ... malformed 블록 ... tool validation 실패로 처리 ... tool_validation_error 로 응답 ... live SSE ... tool_validation_error 이벤트로 스트림을 종료.
|
||||
agent-contract/outer/openai-compatible-api.md:230: raw <tool_call>/{{...}} 블록과 <|mask_end|> 같은 sentinel ... content/SSE delta에 노출하지 않는다.
|
||||
# docs/openai-compatible-api-contract.md : 매칭 0건 (pointer 문서, 원문 미복제 확인)
|
||||
```
|
||||
|
||||
### API-2 중간 검증
|
||||
```bash
|
||||
$ rg --sort path -n "raw text tool|tool_validation_error|Pi/Cline|mask_end|agent-test/runs" docs/edge-local-dev-guide.md agent-test/dev/edge-smoke.md
|
||||
docs/edge-local-dev-guide.md:281,295,304-306,309 (Raw text tool-call boundary smoke 절차/판정/evidence 위치)
|
||||
agent-test/dev/edge-smoke.md:109 (필수 검증: Pi/Cline형 tools[] non-stream/stream smoke)
|
||||
agent-test/dev/edge-smoke.md:123-124 (판정 기준 + agent-test/runs evidence 위치)
|
||||
```
|
||||
|
||||
### API-3 원격 preflight / sync / rebuild / restart
|
||||
```bash
|
||||
# preflight (재빌드 전): 원격 stale 확인
|
||||
$ ssh toki@toki-labs.com '... git log --oneline -1; ports; cp status'
|
||||
b55d1a2 fix: document and verify provider priority leveling # stale (pre-01)
|
||||
port 18001/18083/18084/19093 LISTEN; cp_status_http=200
|
||||
|
||||
# clean-sync → origin/main(01 포함) + Edge 재빌드 + config check
|
||||
$ ssh toki@toki-labs.com 'zsh -lc "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; cd build/dev-runtime; ./bin/edge --config edge.yaml config check"'
|
||||
HEAD is now at 59e55d4 feat: openai-compatible tool call boundary hardening & knowledge tool optimization
|
||||
edge binary rebuilt: -rwxr-xr-x 22026946 Jul 4 18:44 build/dev-runtime/bin/edge (go1.26.3)
|
||||
OK edge.yaml
|
||||
|
||||
# Edge serve 재시작 (old pid 97852 → new pid 21555) + reconnect/health
|
||||
$ ssh toki@toki-labs.com '... kill $(cat edge.pid); nohup ./bin/edge --config edge.yaml serve ...'
|
||||
healthz_http=200
|
||||
CP status nodes: mac-codex-node connected=true, gx10-vllm-node connected=true, onexplayer-lemonade-node connected=true (모두 in_flight=0)
|
||||
/v1/models: {"data":[{"id":"qwen3.6:35b","object":"model","owned_by":"iop"}]} # (bearer token 인증, 원문 미기록)
|
||||
|
||||
# 최종 remote 상태
|
||||
$ ssh toki@toki-labs.com '... git log --oneline -1; config refresh --help; cp status'
|
||||
59e55d4 ...; refresh_help_ok; cp_status=200
|
||||
```
|
||||
|
||||
### API-3 credentialed boundary smoke (token redacted; evidence dir gitignored)
|
||||
```bash
|
||||
# 원격 실행. token은 edge.yaml에서 셸 변수로만 추출, Authorization 헤더 인라인 주입, 파일/로그/보고 미기록.
|
||||
# evidence: (원격, gitignored) build/dev-runtime/logs/boundary-smoke-20260704_184616/
|
||||
|
||||
# 1) known tool, non-stream → POST /v1/chat/completions (tools=[shell], tool_choice=auto)
|
||||
finish_reason=tool_calls has_tool_calls=True names=['shell'] first_args={"commands":["ls -la /tmp"]}
|
||||
raw_leak_tool_call_tag=False raw_leak_mask_end=False content_preview=(empty)
|
||||
|
||||
# 2) known tool, stream → "stream":true
|
||||
sse_has_tool_calls_delta=2 sse_finish_tool_calls=1 sse_done=1
|
||||
sse_raw_tool_call_tag=0 sse_raw_mask_end=0 sse_validation_error=0
|
||||
|
||||
# 3) unknown tool, 자유 유도, non-stream → 모델이 산문 출력 (구조 블록 아님)
|
||||
finish_reason=stop has_tool_calls=False raw_leak_tool_call_tag=False raw_leak_mask_end=False
|
||||
content_preview=lookup_weather(city="Seoul") # {{}}/<tool_call> 블록 아님 → content로 반환, leak 없음
|
||||
|
||||
# 4) unknown tool, literal <tool_call> 강제, non-stream → 차단 확인
|
||||
error_type=tool_validation_error error_msg=tool "lookup_weather" is not in request tools
|
||||
has_tool_calls=False finish_reason=None raw_leak_tool_call_tag=False raw_leak_mask_end=False content_preview=(empty)
|
||||
|
||||
# evidence 파일 token 누출 검사: grep "$TOKEN" logs/boundary-smoke-20260704_184616/ → 0건
|
||||
```
|
||||
|
||||
### 최종 검증
|
||||
```bash
|
||||
$ go test ./apps/edge/internal/openai -count=1
|
||||
ok iop/apps/edge/internal/openai 1.567s
|
||||
|
||||
# 결정적 차단 경로 커버리지 (targeted, 22 tests)
|
||||
$ go test ./apps/edge/internal/openai -run 'TestChatCompletions.*(Unknown|Malformed|TextToolCall|Synthes)|TestValidateToolCallResponse|TestChatCompletionsProviderStreamBlocksMalformedTextToolCall' -count=1 -v
|
||||
--- PASS x22 (Synthesizes*, Retries*/Fails* Malformed·Unknown, ProviderStreamBlocks Unknown·Malformed, LeavesUnknown*AsContent, ValidatesEveryToolCall, SentinelDrop ...)
|
||||
ok iop/apps/edge/internal/openai 0.011s
|
||||
|
||||
$ rg --sort path -n "tool_validation_error|raw text tool|mask_end|Pi/Cline" agent-contract/outer/openai-compatible-api.md docs/edge-local-dev-guide.md agent-test/dev/edge-smoke.md
|
||||
# 3개 위치 모두 매칭 (계약/dev 가이드/dev test-rule)
|
||||
|
||||
# credentialed dev-runtime evidence: 위 "API-3 credentialed boundary smoke" 참조
|
||||
# saved output path (원격, gitignored): build/dev-runtime/logs/boundary-smoke-20260704_184616/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?**
|
||||
> If anything is blank, go back and fill it in before saving this file.
|
||||
|
||||
## 코드리뷰 결과
|
||||
|
||||
- 종합 판정: PASS
|
||||
- 차원별 평가:
|
||||
- correctness: Pass
|
||||
- completeness: Pass
|
||||
- test coverage: Pass
|
||||
- API contract: Pass
|
||||
- code quality: Pass
|
||||
- implementation deviation: Pass
|
||||
- verification trust: Pass
|
||||
- spec conformance: Pass
|
||||
- 발견된 문제:
|
||||
- Nit (fixed): `agent-task/m-openai-compatible-tool-call-boundary-hardening/02+01_contract_dev_smoke/PLAN-cloud-G07.md:1`의 stray `현재` 접두어를 제거했다.
|
||||
- Nit (fixed): `docs/edge-local-dev-guide.md:286`의 secret placeholder export 예시를 제거하고, `docs/edge-local-dev-guide.md:294`의 stream smoke 및 `docs/edge-local-dev-guide.md:300`의 deterministic unknown raw block fixture를 명시했다.
|
||||
- 다음 단계: PASS 종결. active PLAN/REVIEW를 log로 아카이브하고 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다.
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
# Complete - m-openai-compatible-tool-call-boundary-hardening/02+01_contract_dev_smoke
|
||||
|
||||
## 완료 일시
|
||||
|
||||
2026-07-04
|
||||
|
||||
## 요약
|
||||
|
||||
OpenAI-compatible raw text tool-call boundary 계약 문서와 dev smoke 기준/evidence 정리를 1회 리뷰 루프에서 PASS로 종결했다.
|
||||
|
||||
## 루프 이력
|
||||
|
||||
| Plan | Review | Verdict | 메모 |
|
||||
|------|--------|---------|------|
|
||||
| `plan_cloud_G07_0.log` | `code_review_cloud_G07_0.log` | PASS | 계약/dev 문서가 provider raw text tool-call 정규화/차단 정책을 반영했고, dev-runtime credentialed smoke evidence와 local Go 검증이 확인됨. |
|
||||
|
||||
## 구현/정리 내용
|
||||
|
||||
- `agent-contract/outer/openai-compatible-api.md`가 `tools[]` 요청의 provider/CLI raw text tool-call 정규화, unknown/malformed 차단, sentinel sanitize 정책을 설명하도록 갱신됐다.
|
||||
- `docs/edge-local-dev-guide.md`와 `agent-test/dev/edge-smoke.md`에 Pi/Cline형 `tools[]` boundary smoke 기준, evidence 위치, secret 마스킹 원칙이 추가됐다.
|
||||
- dev-runtime Edge를 01 완료 source로 동기화하고 Edge binary rebuild/restart 후 known tool 정규화, known stream delta, unknown raw block 차단 evidence를 남겼다.
|
||||
- 리뷰 중 Nit로 PLAN stray prefix와 dev guide secret placeholder/weak unknown fixture를 직접 정리했다.
|
||||
|
||||
## 최종 검증
|
||||
|
||||
- `go test ./apps/edge/internal/openai -count=1` - PASS; `ok iop/apps/edge/internal/openai 1.568s`
|
||||
- `go test ./apps/edge/internal/openai -run 'TestChatCompletions.*(Unknown|Malformed|TextToolCall|Synthes)|TestValidateToolCallResponse|TestChatCompletionsProviderStreamBlocksMalformedTextToolCall' -count=1 -v` - PASS; targeted 22 tests passed, `ok iop/apps/edge/internal/openai 0.010s`
|
||||
- `rg --sort path -n "tool_validation_error|raw text tool|mask_end|Pi/Cline" agent-contract/outer/openai-compatible-api.md docs/edge-local-dev-guide.md agent-test/dev/edge-smoke.md` - PASS; 계약/dev guide/dev test-rule 모두 매칭
|
||||
- `ssh -o BatchMode=yes -o ConnectTimeout=5 toki@toki-labs.com` non-secret remote preflight/evidence shell assertions - PASS; HEAD `59e55d4`, remote checkout clean on `main` tracking `origin/main`, Edge binary/refresh help OK, Control Plane status HTTP 200, evidence dir `build/dev-runtime/logs/boundary-smoke-20260704_184616/` 존재, `auth_marker_files=0`, `tool_validation_error`와 `tool_calls` marker 확인
|
||||
- `ssh -o BatchMode=yes -o ConnectTimeout=5 toki@toki-labs.com` non-secret response leak marker file assertions - PASS; `<tool_call>` marker는 `req_unknown_forced.json` 요청 fixture에만 존재하고 response/evidence output에는 `<|mask_end|>`, `Authorization`, `Bearer` marker 없음
|
||||
|
||||
## Roadmap Completion
|
||||
|
||||
- Milestone: `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/openai-compatible-tool-call-boundary-hardening.md`
|
||||
- Completed task ids:
|
||||
- `client-smoke`: PASS; evidence=`agent-task/archive/2026/07/m-openai-compatible-tool-call-boundary-hardening/02+01_contract_dev_smoke/plan_cloud_G07_0.log`, `agent-task/archive/2026/07/m-openai-compatible-tool-call-boundary-hardening/02+01_contract_dev_smoke/code_review_cloud_G07_0.log`; verification=`remote dev-runtime evidence dir build/dev-runtime/logs/boundary-smoke-20260704_184616/`, `ssh non-secret remote preflight/evidence shell assertions`
|
||||
- `contract-docs`: PASS; evidence=`agent-task/archive/2026/07/m-openai-compatible-tool-call-boundary-hardening/02+01_contract_dev_smoke/plan_cloud_G07_0.log`, `agent-task/archive/2026/07/m-openai-compatible-tool-call-boundary-hardening/02+01_contract_dev_smoke/code_review_cloud_G07_0.log`; verification=`rg --sort path -n "tool_validation_error|raw text tool|mask_end|Pi/Cline" agent-contract/outer/openai-compatible-api.md docs/edge-local-dev-guide.md agent-test/dev/edge-smoke.md`
|
||||
- Not completed task ids: 없음
|
||||
|
||||
## 잔여 Nit
|
||||
|
||||
- 없음
|
||||
|
||||
## 후속 작업
|
||||
|
||||
- 없음
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
<!-- task=m-openai-compatible-tool-call-boundary-hardening/02+01_contract_dev_smoke plan=0 tag=API -->
|
||||
|
||||
# Code Review Reference - API
|
||||
|
||||
> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
|
||||
> The task is NOT complete until every implementation-owned section below is filled in.
|
||||
> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving.
|
||||
> Fill implementation-owned sections, then stop with active files in place and report ready for review.
|
||||
> If implementation is blocked by a selected Milestone `구현 잠금 > 결정 필요` item, fill `사용자 리뷰 요청` with linked evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`.
|
||||
> Do not ask the user directly, present choices in chat, or call `request_user_input` during implementation.
|
||||
> Finalization is review-agent-only.
|
||||
|
||||
## 개요
|
||||
|
||||
date=2026-07-04
|
||||
task=m-openai-compatible-tool-call-boundary-hardening/02+01_contract_dev_smoke, plan=0, tag=API
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/openai-compatible-tool-call-boundary-hardening.md`
|
||||
- Task ids:
|
||||
- `client-smoke`: Pi/Cline형 긴 agent prompt와 `tools[]` dev Edge 호출 evidence
|
||||
- `contract-docs`: OpenAI-compatible 계약 문서와 dev 운영 문서 정책 설명
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 이 파일을 읽는 리뷰 에이전트에게
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.
|
||||
|
||||
각 항목의 구현을 실제 파일과 evidence path에 대조하고, secret 원문이 tracked 파일이나 review output에 남지 않았는지 확인하세요.
|
||||
|
||||
1. 판정을 append한다.
|
||||
2. `CODE_REVIEW-cloud-G07.md`와 `PLAN-cloud-G07.md`를 `.log`로 아카이브한다.
|
||||
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 archive로 이동한다.
|
||||
4. PASS이고 task group이 `m-openai-compatible-tool-call-boundary-hardening`이면 완료 이벤트 메타데이터를 보고한다. roadmap 수정은 런타임 책임이다.
|
||||
|
||||
---
|
||||
|
||||
## 구현 항목별 완료 여부
|
||||
|
||||
| 항목 | 완료 여부 |
|
||||
|------|---------|
|
||||
| [API-1] Contract Policy Update | [ ] |
|
||||
| [API-2] Dev Smoke Guide And Test Rule | [ ] |
|
||||
| [API-3] Credentialed Dev Runtime Evidence | [ ] |
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] 01번 `agent-task/m-openai-compatible-tool-call-boundary-hardening/01_provider_text_boundary/complete.log`가 존재하고 Roadmap Completion에 `provider-raw-parse`, `unknown-tool-error`, `stream-flush-guard`, `native-preserve`가 포함됐는지 확인한다.
|
||||
- [ ] `agent-contract/outer/openai-compatible-api.md`가 provider route/provider-pool raw text tool-call 후보의 구조화/차단, unknown/malformed 처리, sentinel sanitize 정책을 설명하도록 갱신한다.
|
||||
- [ ] `docs/edge-local-dev-guide.md`와 `agent-test/dev/edge-smoke.md`에 dev-runtime Pi/Cline형 `tools[]` smoke 기준, evidence 보관 위치, secret 마스킹 원칙을 추가한다.
|
||||
- [ ] 원격 dev-runtime source를 01 완료 source와 동기화하고 dev-runtime Edge/Node binaries를 rebuild한 뒤 `config check`와 필요한 refresh/restart를 수행한다.
|
||||
- [ ] dev Edge OpenAI-compatible endpoint에 긴 agent prompt와 `tools[]` 요청을 호출해 valid raw text는 `tool_calls`, unknown hallucination은 `tool_validation_error` 또는 retry 후 차단으로 끝나며 raw block이 body/SSE에 남지 않는 evidence를 남긴다.
|
||||
- [ ] `go test ./apps/edge/internal/openai -count=1`와 문서/evidence 검증 명령을 실행한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 코드리뷰 전용 체크리스트
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
|
||||
|
||||
- [ ] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
|
||||
- [ ] active `CODE_REVIEW-*-G??.md`를 `code_review_cloud_G07_N.log`로 아카이브한다.
|
||||
- [ ] active `PLAN-*-G??.md`를 `plan_cloud_G07_M.log`로 아카이브한다.
|
||||
- [ ] PASS이면 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
|
||||
- [ ] PASS이면 active task 디렉터리를 `agent-task/archive/YYYY/MM/m-openai-compatible-tool-call-boundary-hardening/02+01_contract_dev_smoke/`로 이동한다.
|
||||
- [ ] PASS이고 task group이 `m-openai-compatible-tool-call-boundary-hardening`이면 런타임 완료 이벤트 메타데이터를 보고하고 roadmap을 직접 수정하지 않는다.
|
||||
- [ ] WARN/FAIL이면 다음 active plan/review 파일 또는 unresolved verification report를 작성한다.
|
||||
|
||||
## 계획 대비 변경 사항
|
||||
|
||||
_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._
|
||||
|
||||
## 주요 설계 결정
|
||||
|
||||
_구현 에이전트가 주요 설계 결정 사항을 기록한다._
|
||||
|
||||
## 사용자 리뷰 요청
|
||||
|
||||
_기본값은 `없음`이다. 구현 중 새 결정이 필요해 보여도 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 이 섹션은 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 차단할 때만 채운다. 외부 환경/secret/서비스 준비, 검증 증거 공백, 반복 실패, 일반 범위 조정은 사용자 리뷰 요청이 아니며 `검증 결과`, `계획 대비 변경 사항`, 또는 code-review의 일반 follow-up plan으로 처리한다._
|
||||
|
||||
- 상태: 없음
|
||||
- 사유 유형: 없음
|
||||
- 연결 대상: 없음
|
||||
- 결정 필요: 없음
|
||||
- 차단 근거: 없음
|
||||
- 실행한 검증/명령: 없음
|
||||
- 자동 후속 불가 이유: 없음
|
||||
- 재개 조건: 없음
|
||||
|
||||
## 리뷰어를 위한 체크포인트
|
||||
|
||||
- 01 predecessor completion이 실제로 충족됐는지 확인한다.
|
||||
- 계약 문서가 `docs/`에 중복 원문을 만들지 않고 `agent-contract`를 source of truth로 유지하는지 확인한다.
|
||||
- dev smoke evidence가 token/secret 원문을 노출하지 않는지 확인한다.
|
||||
- raw `<tool_call>`, unknown tool hallucination, `<|mask_end|>` leak 차단 evidence가 남았는지 확인한다.
|
||||
|
||||
## 검증 결과
|
||||
|
||||
_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._
|
||||
|
||||
### API-1 중간 검증
|
||||
```bash
|
||||
$ rg --sort path -n "provider route에서|raw text|tool_validation_error|mask_end" agent-contract/outer/openai-compatible-api.md docs/openai-compatible-api-contract.md
|
||||
(output)
|
||||
```
|
||||
|
||||
### API-2 중간 검증
|
||||
```bash
|
||||
$ rg --sort path -n "raw text tool|tool_validation_error|Pi/Cline|mask_end|agent-test/runs" docs/edge-local-dev-guide.md agent-test/dev/edge-smoke.md
|
||||
(output)
|
||||
```
|
||||
|
||||
### API-3 중간 검증
|
||||
```bash
|
||||
$ ssh -o BatchMode=yes -o ConnectTimeout=5 toki@toki-labs.com 'cd /Users/toki/agent-work/iop-dev && git status --branch --short && git log --oneline -1 && test -x build/dev-runtime/bin/edge && build/dev-runtime/bin/edge --config build/dev-runtime/edge.yaml config refresh --help >/dev/null && for p in 18001 18083 18084 19093; do lsof -nP -iTCP:$p -sTCP:LISTEN >/dev/null || exit 20; done && curl -fsS -m 3 -o /dev/null http://127.0.0.1:18001/edges/edge-toki-labs-dev/status'
|
||||
(output)
|
||||
```
|
||||
|
||||
### 최종 검증
|
||||
```bash
|
||||
$ go test ./apps/edge/internal/openai -count=1
|
||||
(output)
|
||||
|
||||
$ rg --sort path -n "tool_validation_error|raw text tool|mask_end|Pi/Cline" agent-contract/outer/openai-compatible-api.md docs/edge-local-dev-guide.md agent-test/dev/edge-smoke.md
|
||||
(output)
|
||||
|
||||
$ <credentialed dev-runtime known/unknown tool request commands with token redacted>
|
||||
(output or saved evidence path)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?**
|
||||
> If anything is blank, go back and fill it in before saving this file.
|
||||
|
|
@ -106,6 +106,7 @@ mac-mlx-vllm provider는 mac-codex-node 소속 resource로, Edge host와 같은
|
|||
- 변경한 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을 덮어쓰지 않는지 확인한다.
|
||||
|
||||
## 보조 검증
|
||||
|
|
@ -119,6 +120,8 @@ mac-mlx-vllm provider는 mac-codex-node 소속 resource로, Edge host와 같은
|
|||
- 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` 회복을 확인한다.
|
||||
|
|
|
|||
|
|
@ -276,6 +276,43 @@ curl -fsS http://toki-labs.com:18083/v1/chat/completions \
|
|||
|
||||
현재 공개 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` 기준을 따른다.
|
||||
|
|
|
|||
Loading…
Reference in a new issue