feat: add Ollama target client, model poller, and native passthrough
Phase 2 of foreman: the daemon now acts as a transparent Ollama proxy. - internal/ollama: Client interface and HTTP implementation for chat (streaming + non-streaming), embed, tags, ps with auth forwarding, NDJSON streaming via bufio.Scanner, and connection vs HTTP error classification via custom error types. - internal/ollama: ModelInventory with background poller for /api/tags and /api/ps, degraded mode on target unreachable with model retention, automatic recovery on reconnect. - internal/server: Passthrough routes (/api/chat, /api/tags, /api/ps, /api/embed, /api/embeddings) with model validation, chat serialization gate (capacity-1 channel), concurrent embedding bypass (ADR-0013), NDJSON streaming with per-chunk flush, and degraded health reporting. - cmd/foreman: Full serve wiring with Ollama client, poller goroutine, embedder warmup (keep_alive:-1), and signal-based shutdown. The Mac is now usable as a go-llm target through foreman. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
package ollama
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ConnectionError wraps a network-level error when the Ollama target is unreachable.
|
||||
// Phase 3 uses this to distinguish connection failures (retry-eligible) from HTTP
|
||||
// errors (usually not retryable).
|
||||
//
|
||||
// Why: callers must differentiate "target is down" from "target returned 4xx/5xx"
|
||||
// to decide on retry strategy.
|
||||
// What: wraps a net-level error and satisfies the error and Unwrap interfaces.
|
||||
// Test: create a ConnectionError, verify errors.Is/As can match it.
|
||||
type ConnectionError struct {
|
||||
URL string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *ConnectionError) Error() string {
|
||||
return fmt.Sprintf("connection to ollama target %s failed: %v", e.URL, e.Err)
|
||||
}
|
||||
|
||||
func (e *ConnectionError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
// HTTPError represents a non-2xx HTTP response from the Ollama target.
|
||||
//
|
||||
// Why: callers need the status code to distinguish client errors (4xx) from
|
||||
// server errors (5xx) and decide on retry logic.
|
||||
// What: holds the HTTP status code and response body for error diagnosis.
|
||||
// Test: create an HTTPError with status 500, verify Error() includes the code.
|
||||
type HTTPError struct {
|
||||
StatusCode int
|
||||
Body string
|
||||
}
|
||||
|
||||
func (e *HTTPError) Error() string {
|
||||
return fmt.Sprintf("ollama target returned HTTP %d: %s", e.StatusCode, e.Body)
|
||||
}
|
||||
Reference in New Issue
Block a user