Repository: ollama/ollama
File: app/ui/ui.go (Go)

Code before:
	Tools        bool   // if true, the server will use single-turn tools to fulfill the user's request
	WebSearch    bool   // if true, the server will use single-turn browser tool to fulfill the user's request
	Agent        bool   // if true, the server will use multi-turn tools to fulfill the user's request
	WorkingDir   string // Working directory for all agent operations

	// Dev is true if the server is running in development mode
	Dev bool
}

func (s *Server) log() *slog.Logger {
	if s.Logger == nil {
		return slog.Default()
	}
	return s.Logger
}

// ollamaProxy creates a reverse proxy handler to the Ollama server
func (s *Server) ollamaProxy() http.Handler {
	var (
		proxy     http.Handler
		proxyOnce sync.Once
		proxyErr  error
	)

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		proxyOnce.Do(func() {
			if err := waitForServer(r.Context()); err != nil {
				proxyErr = err
				return
			}

			target := envconfig.Host()
			s.log().Info("configuring ollama proxy", "target", target.String())

			p := httputil.NewSingleHostReverseProxy(target)

			originalDirector := p.Director
			p.Director = func(req *http.Request) {
				originalDirector(req)
				req.Host = target.Host
				s.log().Debug("proxying request", "method", req.Method, "path", req.URL.Path, "target", target.Host)
			}

			p.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) {
				s.log().Error("proxy error", "error", err, "path", r.URL.Path, "target", target.String())
				http.Error(w, "proxy error: "+err.Error(), http.StatusBadGateway)
			}

			proxy = p
		})


Review comment (bug):
Once proxyOnce.Do() executes with an error, proxyErr is permanently set. If the server starts late (after the first request), subsequent requests will still fail with "Ollama server is not ready" until the app restarts.

I think we want to have some retry logic here.

Code after:
	WebSearch    bool   // if true, the server will use single-turn browser tool to fulfill the user's request
	Agent        bool   // if true, the server will use multi-turn tools to fulfill the user's request
	WorkingDir   string // Working directory for all agent operations

	// Dev is true if the server is running in development mode
	Dev bool
}

func (s *Server) log() *slog.Logger {
	if s.Logger == nil {
		return slog.Default()
	}
	return s.Logger
}

// ollamaProxy creates a reverse proxy handler to the Ollama server
func (s *Server) ollamaProxy() http.Handler {
	var (
		proxy   http.Handler
		proxyMu sync.Mutex
	)

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		proxyMu.Lock()
		p := proxy
		proxyMu.Unlock()

		if p == nil {
			proxyMu.Lock()
			if proxy == nil {
				var err error
				for i := range 2 {
					if i > 0 {
						s.log().Warn("ollama server not ready, retrying", "attempt", i+1)
						time.Sleep(1 * time.Second)
					}

					err = WaitForServer(context.Background(), 10*time.Second)
					if err == nil {
						break
					}
				}

				if err != nil {
					proxyMu.Unlock()
					s.log().Error("ollama server not ready after retries", "error", err)
					http.Error(w, "Ollama server is not ready", http.StatusServiceUnavailable)
					return
				}

				target := envconfig.Host()