fix(openai): Review 읽기를 handoff 쓰기와 구분한다
Work 도구 호출에 review 경로가 포함됐다는 이유만으로 handoff write로 오분류하지 않는다. 실제 write content가 있는 호출만 handoff로 판정하고 단순 read/inspection은 caller 도구로 통과시킨다.
This commit is contained in:
parent
64ab12b895
commit
c051529c05
2 changed files with 35 additions and 16 deletions
|
|
@ -981,29 +981,19 @@ func (s *hotPathLightStore) classifyLocalOutput(requestID, ownerEdgeID string, o
|
|||
return "", nil
|
||||
}
|
||||
paths := newReservedPaths(requestID)
|
||||
reviewWrites := 0
|
||||
reviewWrites := make([]normalizedToolCall, 0, 1)
|
||||
for _, call := range output.ToolCalls {
|
||||
for _, observed := range reservedPathsFromToolCall(call) {
|
||||
if cleanRelativePath(observed) == cleanRelativePath(paths.ReviewPath) {
|
||||
reviewWrites++
|
||||
}
|
||||
if isWorkerReviewHandoffCall(record.binding, paths, call) {
|
||||
reviewWrites = append(reviewWrites, call)
|
||||
}
|
||||
}
|
||||
if reviewWrites == 0 {
|
||||
if len(reviewWrites) == 0 {
|
||||
return hotPathPendingLocalTools, nil
|
||||
}
|
||||
if reviewWrites != 1 || !record.localPlanRead {
|
||||
if len(reviewWrites) != 1 || !record.localPlanRead {
|
||||
return "", fmt.Errorf("worker review handoff must contain one exact review write after reading the plan")
|
||||
}
|
||||
var reviewCall normalizedToolCall
|
||||
for _, call := range output.ToolCalls {
|
||||
for _, observed := range reservedPathsFromToolCall(call) {
|
||||
if cleanRelativePath(observed) == cleanRelativePath(paths.ReviewPath) {
|
||||
reviewCall = call
|
||||
}
|
||||
}
|
||||
}
|
||||
content, err := artifactWriteContent(record.binding, reviewCall)
|
||||
content, err := artifactWriteContent(record.binding, reviewWrites[0])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
@ -1013,6 +1003,22 @@ func (s *hotPathLightStore) classifyLocalOutput(requestID, ownerEdgeID string, o
|
|||
return hotPathPendingLocalHandoff, nil
|
||||
}
|
||||
|
||||
func isWorkerReviewHandoffCall(binding *workspaceBinding, paths reservedPaths, call normalizedToolCall) bool {
|
||||
write := binding.operation(opKindWrite)
|
||||
if write == nil || strings.TrimSpace(write.contentField) == "" {
|
||||
return false
|
||||
}
|
||||
if _, ok := lookupMappedArgument(call.Arguments, write.contentField); !ok {
|
||||
return false
|
||||
}
|
||||
for _, observed := range reservedPathsFromToolCall(call) {
|
||||
if cleanRelativePath(observed) == cleanRelativePath(paths.ReviewPath) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type hotPathReviewEvidence struct {
|
||||
planRead bool
|
||||
reviewRead bool
|
||||
|
|
|
|||
|
|
@ -78,6 +78,19 @@ func TestHotPathStageOrdinaryWorkspacePathPassesThrough(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestWorkerReviewReadIsNotClassifiedAsHandoffWrite(t *testing.T) {
|
||||
binding := mustBinding(t, fullWorkspaceAlternative("workspace", "workspace", false), []any{openAIChatTool("workspace", structuredSchema())})
|
||||
paths := newReservedPaths("req_review_read")
|
||||
read := normalizedToolCall{Name: "workspace", Arguments: map[string]any{"path": paths.ReviewPath}}
|
||||
if isWorkerReviewHandoffCall(binding, paths, read) {
|
||||
t.Fatal("review read was classified as a handoff write")
|
||||
}
|
||||
write := normalizedToolCall{Name: "workspace", Arguments: map[string]any{"path": paths.ReviewPath, "content": testCompletedReviewText()}}
|
||||
if !isWorkerReviewHandoffCall(binding, paths, write) {
|
||||
t.Fatal("review write was not classified as a handoff write")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWorkerProviderReceivesOnlyCallerTools(t *testing.T) {
|
||||
callerTools := []any{openAIChatTool("run_command", commandSchema())}
|
||||
tools := hotPathStageProviderTools(hotPathDispatchSnapshot{Phase: hotPathPhaseLocalActive, Tools: callerTools})
|
||||
|
|
|
|||
Loading…
Reference in a new issue