package openai import ( "net/http" "strings" "time" "iop/apps/edge/internal/authprojection" ) func (s *Server) routes() *http.ServeMux { mux := http.NewServeMux() mux.HandleFunc("/healthz", s.handleHealthz) mux.HandleFunc("/v1/models", s.withAuth(s.handleModels)) mux.HandleFunc("/v1/chat/completions", s.withAuth(s.handleChatCompletions)) mux.HandleFunc("/v1/responses", s.withAuth(s.handleResponses)) s.registerAnthropicRoutes(mux) mux.HandleFunc("/api/", s.withAuth(s.handleOllamaAPI)) return mux } func (s *Server) registerAnthropicRoutes(mux *http.ServeMux) { for _, path := range []string{"/v1/messages", "/anthropic/v1/messages"} { mux.HandleFunc(path, s.withAuth(s.handleAnthropicMessages)) } for _, path := range []string{"/v1/messages/count_tokens", "/anthropic/v1/messages/count_tokens"} { mux.HandleFunc(path, s.withAuth(s.handleAnthropicCountTokens)) } mux.HandleFunc("/anthropic/v1/models", s.withAuth(s.handleModels)) } func (s *Server) withAuth(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { principal, view, ok := s.authenticatePrincipal(r) if !ok { s.writeAuthenticationFailure(w, r) return } ctx := withPrincipal(r.Context(), principal) if view.State == authprojection.StateFresh { ctx = withAuthenticatedProjectionView(ctx, view) } if s.managedCredentialPlane() && hasLegacyProviderCredential(r, s.cfg.ProviderAuth.FromHeader) { s.writeCallerProviderCredentialRejection(w, r) return } next(w, r.WithContext(ctx)) } } func (s *Server) writeCallerProviderCredentialRejection(w http.ResponseWriter, r *http.Request) { const message = "caller provider credentials are not allowed in managed credential mode" if isAnthropicRequest(r) { writeAnthropicError(w, http.StatusBadRequest, "invalid_request_error", message) return } writeError(w, http.StatusBadRequest, "invalid_request_error", message) } func (s *Server) writeAuthenticationFailure(w http.ResponseWriter, r *http.Request) { w.Header().Set("WWW-Authenticate", `Bearer realm="iop-openai"`) if isAnthropicRequest(r) { writeAnthropicError(w, http.StatusUnauthorized, "authentication_error", "authentication failed") return } writeError(w, http.StatusUnauthorized, "unauthorized", "unauthorized") } func (s *Server) handleHealthz(w http.ResponseWriter, _ *http.Request) { writeJSON(w, http.StatusOK, map[string]string{"status": "ok"}) } func (s *Server) handleModels(w http.ResponseWriter, r *http.Request) { anthropic := strings.HasPrefix(r.URL.Path, "/anthropic/v1/") || strings.TrimSpace(r.Header.Get(anthropicVersionHeader)) != "" if r.Method != http.MethodGet { if anthropic { writeAnthropicError(w, http.StatusMethodNotAllowed, "invalid_request_error", "method not allowed") } else { writeError(w, http.StatusMethodNotAllowed, "method_not_allowed", "method not allowed") } return } models, err := s.advertisedModelsForPrincipal(r.Context()) if err != nil { s.writeManagedRouteError(w, r, err) return } if anthropic { if err := validateAnthropicHeaders(r, false); err != nil { writeAnthropicError(w, http.StatusBadRequest, "invalid_request_error", err.Error()) return } writeAnthropicModels(w, models) return } data := make([]openAIModel, 0, len(models)) for _, model := range models { data = append(data, openAIModel{ ID: model.ID, Object: "model", Created: time.Now().Unix(), OwnedBy: "iop", }) } writeJSON(w, http.StatusOK, openAIModelsResponse{Object: "list", Data: data}) } type advertisedModel struct { ID string DisplayName string } func (s *Server) advertisedModels() []advertisedModel { var models []advertisedModel modelCatalog := s.modelCatalogSnapshot() if len(modelCatalog) > 0 { // Provider pool catalog takes priority over legacy model_routes. for _, entry := range modelCatalog { if id := strings.TrimSpace(entry.ID); id != "" { displayName := strings.TrimSpace(entry.DisplayName) if displayName == "" { displayName = id } models = append(models, advertisedModel{ID: id, DisplayName: displayName}) } } } else if len(s.cfg.ModelRoutes) > 0 { for _, route := range s.cfg.ModelRoutes { id := strings.TrimSpace(route.Model) if id != "" && route.Target != "" { models = append(models, advertisedModel{ID: id, DisplayName: id}) } } } else { modelIDs := s.cfg.Models if len(modelIDs) == 0 && s.cfg.Target != "" { modelIDs = []string{s.cfg.Target} } for _, id := range modelIDs { id = strings.TrimSpace(id) if id != "" { models = append(models, advertisedModel{ID: id, DisplayName: id}) } } } return models }