Skip to content

Commit

Permalink
Merge pull request #42 from Lanture1064/dev
Browse files Browse the repository at this point in the history
feat: add LLM controller
  • Loading branch information
bjwswang authored Aug 23, 2023
2 parents d4a9e01 + 9a210ca commit 75639d3
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 2 deletions.
2 changes: 2 additions & 0 deletions api/v1alpha1/condition.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ const (
TypeUnknown ConditionType = "Unknown"
// TypeDone resources are believed to be processed
TypeDone ConditionType = "Done"
// TypeUnavailable resources are unavailable
TypeUnavailable ConditionType = "Unavailable"
)

// A ConditionReason represents the reason a resource is in a condition.
Expand Down
110 changes: 108 additions & 2 deletions controllers/llm_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,19 @@ package controllers

import (
"context"
"fmt"
"net/http"

"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/reconcile"

arcadiav1alpha1 "github.com/kubeagi/arcadia/api/v1alpha1"
)
Expand All @@ -47,10 +55,26 @@ type LLMReconciler struct {
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/[email protected]/pkg/reconcile
func (r *LLMReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
_ = log.FromContext(ctx)
logger := log.FromContext(ctx)
logger.Info("Reconciling LLM resource")

// TODO(user): your logic here
// Fetch the LLM instance
instance := &arcadiav1alpha1.LLM{}
err := r.Get(ctx, req.NamespacedName, instance)
if err != nil {
if errors.IsNotFound(err) {
// LLM instance has been deleted.
return reconcile.Result{}, nil
}
return ctrl.Result{}, client.IgnoreNotFound(err)
}

err = r.CheckLLM(ctx, logger, instance)
if err != nil {
return ctrl.Result{}, err
}

logger.Info("Instance is updated and synchronized")
return ctrl.Result{}, nil
}

Expand All @@ -60,3 +84,85 @@ func (r *LLMReconciler) SetupWithManager(mgr ctrl.Manager) error {
For(&arcadiav1alpha1.LLM{}).
Complete(r)
}

// CheckLLM updates new LLM instance.
func (r *LLMReconciler) CheckLLM(ctx context.Context, logger logr.Logger, instance *arcadiav1alpha1.LLM) error {
logger.Info("Checking LLM instance")
// Check new URL/Auth availability
err := r.TestLLMAvailability(ctx, instance, logger)
if err != nil {
// Set status to unavailable
instance.Status.SetConditions(arcadiav1alpha1.Condition{
Type: arcadiav1alpha1.TypeUnavailable,
Status: corev1.ConditionFalse,
Reason: arcadiav1alpha1.ReasonUnavailable,
Message: err.Error(),
LastTransitionTime: metav1.Now(),
})
} else {
// Set status to available
instance.Status.SetConditions(arcadiav1alpha1.Condition{
Type: arcadiav1alpha1.TypeReady,
Status: corev1.ConditionTrue,
Reason: arcadiav1alpha1.ReasonAvailable,
Message: "Available",
LastTransitionTime: metav1.Now(),
LastSuccessfulTime: metav1.Now(),
})
}
return r.Client.Status().Update(ctx, instance)
}

// TestLLMAvailability tests LLM availability.
func (r *LLMReconciler) TestLLMAvailability(ctx context.Context, instance *arcadiav1alpha1.LLM, logger logr.Logger) error {
logger.Info("Testing LLM availability")

//TODO: change URL & request for different types of LLM instance
// For openai instance, we use the "GET model" api.
// For Zhipuai instance, we send a standard async request.
testURL := instance.Spec.URL + "/v1/models"

if instance.Spec.Auth == "" {
return fmt.Errorf("auth is empty")
}

// get auth by secret name
var auth string
secret := &corev1.Secret{}
err := r.Get(ctx, types.NamespacedName{Name: instance.Spec.Auth, Namespace: instance.Namespace}, secret)
if err != nil {
return err
}

auth = "Bearer " + string(secret.Data["apiKey"])

err = SendTestRequest("GET", testURL, auth)
if err != nil {
return err
}

return nil
}

func SendTestRequest(method string, url string, auth string) error {
req, err := http.NewRequest(method, url, nil)
if err != nil {
return err
}

req.Header.Set("Authorization", auth)
req.Header.Set("Content-Type", "application/json")

cli := &http.Client{}
resp, err := cli.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return fmt.Errorf("returns unexpected status code: %d", resp.StatusCode)
}

return nil
}

0 comments on commit 75639d3

Please sign in to comment.