Add Ollama support and fix macOS build

ober

769cf5d0f88e32012f2d494cf8abb55e47dd33e3

diff --git a/lib/jcode/provider/provider.sls b/lib/jcode/provider/provider.sls
index 9cacf80..3e4e8eb 100644
--- a/lib/jcode/provider/provider.sls
+++ b/lib/jcode/provider/provider.sls
@@ -60,7 +60,8 @@
          [else
           (error 'provider-default-url "Unknown provider" name)]))
   (def (provider-chat provider messages tools)
-       (unless (provider-api-key provider)
+       (unless (or (provider-api-key provider)
+                   (equal? (provider-name provider) "ollama"))
          (error 'provider-chat
            (format
              "No API key configured for provider '~a'. Set the appropriate env var or add it to jcode.json."
@@ -287,7 +288,7 @@
                             [body (port-read-all in)])
                        (values status body)))
                    (lambda () (close-port in) (close-port out))))))))
-  (def (http-post-stream url headers body-json line-cb)
+  (def (jcode-http-post-stream url headers body-json line-cb)
        (let-values ([(scheme host port path)
                      (parse-url-parts url)])
          (let ([req (build-http-request "POST" path host headers
@@ -303,7 +304,7 @@
                             [_headers (read-tls-headers conn)])
                        (unless (= status 200)
                          (let ([body (tls-read-all conn)])
-                           (error 'http-post-stream
+                           (error 'jcode-http-post-stream
                              (format "API error ~a: ~a" status body))))
                        (let loop ()
                          (let ([line (tls-read-line conn)])
@@ -323,7 +324,7 @@
                             [_headers (port-read-headers in)])
                        (unless (= status 200)
                          (let ([body (port-read-all in)])
-                           (error 'http-post-stream
+                           (error 'jcode-http-post-stream
                              (format "API error ~a: ~a" status body))))
                        (let loop ()
                          (let ([c (peek-char in)])
@@ -676,7 +677,7 @@
                          "stream-request"
                          `((url . ,url)
                             (body-len . ,(string-length body-json))))]
-                [http-status (http-post-stream
+                [http-status (jcode-http-post-stream
                                url
                                headers
                                body-json
@@ -703,7 +704,7 @@
                                      (cond
                                        [(equal? data "[DONE]") (void)]
                                        [else
-                                        (let ([json (guard (e [list #t #f])
+                                        (let ([json (guard (e [#t #f])
                                                       (string->json-object
                                                         data))])
                                           (when (and json
@@ -856,7 +857,7 @@
               [current-idx (make-parameter #f)]
               [usage-acc (make-hash-table)]
               [event-type #f])
-         (let ([http-status (http-post-stream
+         (let ([http-status (jcode-http-post-stream
                               url
                               headers
                               (json-object->string body)
@@ -883,7 +884,7 @@
                                                (string-length line)))]))
                                       lines)
                                     (when (and event-type data-str)
-                                      (let ([json (guard (e [list #t #f])
+                                      (let ([json (guard (e [#t #f])
                                                     (string->json-object
                                                       data-str))])
                                         (when (and json (hash-table? json))
@@ -1033,7 +1034,8 @@
                  (or (hash-get usage-acc "output_tokens") 0))
                (cons 'cost 0))))))
   (def (provider-stream-chat provider messages tools token-cb)
-       (unless (provider-api-key provider)
+       (unless (or (provider-api-key provider)
+                   (equal? (provider-name provider) "ollama"))
          (error 'provider-stream-chat
            (format
              "No API key configured for provider '~a'. Set the appropriate env var or add it to jcode.json."
diff --git a/src/jcode/provider/provider.ss b/src/jcode/provider/provider.ss
index 07e4281..1298c90 100644
--- a/src/jcode/provider/provider.ss
+++ b/src/jcode/provider/provider.ss
@@ -67,7 +67,8 @@
     (else (error 'provider-default-url "Unknown provider" name))))
 
 (def (provider-chat provider messages tools)
-  (unless (provider-api-key provider)
+  (unless (or (provider-api-key provider)
+              (equal? (provider-name provider) "ollama"))
     (error 'provider-chat
       (format "No API key configured for provider '~a'. Set the appropriate env var or add it to jcode.json."
         (provider-name provider))))
@@ -275,7 +276,7 @@
 
 ;; Streaming HTTP POST: calls line-cb with each line of the response body.
 ;; Used for SSE (Server-Sent Events) streaming from LLM APIs.
-(def (http-post-stream url headers body-json line-cb)
+(def (jcode-http-post-stream url headers body-json line-cb)
   (let-values (((scheme host port path) (parse-url-parts url)))
     (let ((req (build-http-request "POST" path host headers body-json)))
       (if (equal? scheme "https")
@@ -290,7 +291,7 @@
                      (_headers (read-tls-headers conn)))
                 (unless (= status 200)
                   (let ((body (tls-read-all conn)))
-                    (error 'http-post-stream
+                    (error 'jcode-http-post-stream
                       (format "API error ~a: ~a" status body))))
                 ;; Read SSE lines until EOF or chunked terminator
                 (let loop ()
@@ -312,7 +313,7 @@
                      (_headers (port-read-headers in)))
                 (unless (= status 200)
                   (let ((body (port-read-all in)))
-                    (error 'http-post-stream
+                    (error 'jcode-http-post-stream
                       (format "API error ~a: ~a" status body))))
                 ;; Read SSE lines
                 (let loop ()
@@ -631,7 +632,7 @@
            (dummy (log-info logger "stream-request"
                     `((url . ,url) (body-len . ,(string-length body-json)))))
            (http-status
-           (http-post-stream url headers body-json
+           (jcode-http-post-stream url headers body-json
       (lambda (event-str)
         (when event-str
           (log-info logger "sse-event" `((data . ,(if (> (string-length event-str) 120)
@@ -733,7 +734,7 @@
          (usage-acc   (make-hash-table))
          (event-type  #f))
     (let ((http-status
-           (http-post-stream url headers (json-object->string body)
+           (jcode-http-post-stream url headers (json-object->string body)
       (lambda (event-str)
         (when event-str
           ;; Anthropic SSE uses multiple lines per event: "event: ...\ndata: ..."
@@ -820,7 +821,8 @@
 ;; provider-stream-chat: stream text tokens to token-cb, accumulate tool calls.
 ;; Returns (values content-str tool-call-list usage-alist).
 (def (provider-stream-chat provider messages tools token-cb)
-  (unless (provider-api-key provider)
+  (unless (or (provider-api-key provider)
+              (equal? (provider-name provider) "ollama"))
     (error 'provider-stream-chat
       (format "No API key configured for provider '~a'. Set the appropriate env var or add it to jcode.json."
         (provider-name provider))))