Accept write alias during forced edit streams
ober
16ac3b58ce6d034cc4df0bc432f3ed767f9cde42
--- a/src/jcode/provider/provider.ss +++ b/src/jcode/provider/provider.ss @@ -1038,6 +1038,13 @@ expected ".")) +(def (forced-tool-name-accepted? expected actual) + (or (string=? actual expected) + (and (string=? expected "edit") + (string=? actual "write")) + (and (string=? expected "write") + (string=? actual "edit")))) + (def (openai-max-tokens provider) (let* ((configured (or (positive-int-value (getenv "JCODE_MAX_TOKENS")) @@ -2537,7 +2544,8 @@ (when name (when (and forced-tool-name (string? name) - (not (string=? name forced-tool-name))) + (not (forced-tool-name-accepted? + forced-tool-name name))) (set-box! forced-tool-mismatch-box (cons forced-tool-name (string-append --- a/test/run.ss +++ b/test/run.ss @@ -13146,6 +13146,48 @@ (let* ([sse-body (string-append + "data: {\"choices\":[{\"index\":0,\"delta\":{\"tool_calls\":[{\"index\":0,\"id\":\"call_write_alias\",\"type\":\"function\",\"function\":{\"name\":\"write\",\"arguments\":\"\"}}]},\"finish_reason\":null}]}\n\n" + "data: {\"choices\":[{\"index\":0,\"delta\":{\"tool_calls\":[{\"index\":0,\"function\":{\"arguments\":\"{\\\"path\\\":\\\"tetris.ss\\\",\\\"content\\\":\\\"alias write ok\\\"}\"}}]},\"finish_reason\":\"tool_calls\"}]}\n\n" + "data: [DONE]\n\n")] + [srv (tcp-listen "127.0.0.1" 0)] + [base-url (format "http://127.0.0.1:~a/v1" (tcp-server-port srv))] + [captured (vector #f)] + [edit-tool (uuid-recovery-tool "edit" (args "path" (args "type" "string")) '("path"))] + [write-tool (uuid-recovery-tool "write" (args "path" (args "type" "string")) '("path"))] + [old-max-tokens (getenv "JCODE_MAX_TOKENS")]) + (dynamic-wind + (lambda () (putenv "JCODE_MAX_TOKENS" "")) + (lambda () + (serve-one-captured-sse! srv captured sse-body) + (let* ([p (make-provider "mlx" "" "deepseek-v4-pro" base-url)] + [result (call-with-values + (lambda () + (parameterize ([current-tool-choice-override + (openai-function-tool-choice "edit")]) + (provider-stream-chat + p + (list (make-user-message "hi")) + (list edit-tool write-tool) + (lambda (_token) #f)))) + (lambda (content tcs usage) (list content tcs usage)))] + [req (vector-ref captured 0)] + [tcs (list-ref result 1)]) + (check! "mlx forced streaming edit accepts write alias content" + (car result) "") + (check! "mlx forced streaming edit accepts write alias call count" + (length tcs) 1) + (check! "mlx forced streaming edit accepts write alias tool" + (tool-call-name (car tcs)) "write") + (check! "mlx forced streaming edit alias request required edit" + (and req + (str-contains? req "\"tool_choice\"") + (str-contains? req "\"name\":\"edit\"")) #t))) + (lambda () + (putenv "JCODE_MAX_TOKENS" (or old-max-tokens "")) + (tcp-close srv)))) + +(let* ([sse-body + (string-append "data: {\"choices\":[{\"index\":0,\"delta\":{\"reasoning_content\":\"I should check balance, but writing instead.\"},\"finish_reason\":null}]}\n\n" "data: {\"choices\":[{\"index\":0,\"delta\":{\"tool_calls\":[{\"index\":0,\"id\":\"call_bad\",\"type\":\"function\",\"function\":{\"name\":\"write\",\"arguments\":\"\"}}]},\"finish_reason\":null}]}\n\n" "data: {\"choices\":[{\"index\":0,\"delta\":{\"tool_calls\":[{\"index\":0,\"function\":{\"arguments\":\"{\\\"path\\\":\\\"tetris.ss\\\",\\\"content\\\":\\\"large ignored write\\\"}\"}}]},\"finish_reason\":null}]}\n\n")]