diff --git a/aml-services/alpha/ailab/Jenkinsfile b/aml-services/alpha/ailab/Jenkinsfile deleted file mode 100644 index 2d827f0d..00000000 --- a/aml-services/alpha/ailab/Jenkinsfile +++ /dev/null @@ -1,35 +0,0 @@ -library 'first-class-pipeline' - -executeFreeleapsPipeline { - serviceName = 'aml-services' - environmentSlug = 'alpha' - serviceGitBranch = 'dev' - serviceGitRepo = "https://gitea.freeleaps.mathmast.com/freeleaps/aml-services.git" - serviceGitRepoType = 'monorepo' - serviceGitCredentialsId = 'freeleaps-repos-gitea-credentails' - executeMode = 'on-demand' - commitMessageLintEnabled = false - components = [ - [ - name: 'ailab', - root: 'apps/ailab', - language: 'python', - dependenciesManager: 'pip', - requirementsFile: 'requirements.txt', - buildCacheEnabled: true, - buildAgentImage: 'python:3.10-slim-buster', - buildArtifacts: ['.'], - lintEnabled: false, - sastEnabled: false, - imageRegistry: 'docker.io', - imageRepository: 'freeleaps', - imageName: 'ailab', - imageBuilder: 'dind', - dockerfilePath: 'Dockerfile', - imageBuildRoot: '.', - imageReleaseArchitectures: ['linux/amd64', 'linux/arm64/v8'], - registryCredentialsId: 'freeleaps-devops-docker-hub-credentials', - semanticReleaseEnabled: true - ] - ] -} \ No newline at end of file diff --git a/aml-services/alpha/ci/Jenkinsfile b/aml-services/alpha/ci/Jenkinsfile new file mode 100644 index 00000000..860edea6 --- /dev/null +++ b/aml-services/alpha/ci/Jenkinsfile @@ -0,0 +1,34 @@ +library 'first-class-pipeline' + +executeFreeleapsPipeline { + serviceName = 'aml-services' + environmentSlug = 'alpha' + serviceGitBranch = 'dev' + serviceGitRepo = "https://gitea.freeleaps.mathmast.com/freeleaps/aml-services.git" + serviceGitRepoType = 'monorepo' + serviceGitCredentialsId = 'aml-services-git-repo-credentials' + executeMode = 'fully' + commitMessageLintEnabled = false + components = [ + [ + name: 'ailab', + root: 'apps/ailab', + language: 'python', + dependenciesManager: 'pip', + buildCacheEnabled: true, + buildAgentImage: 'python:3.10-slim-bullseye', + buildArtifacts: ['.'], + lintEnabled: false, + sastEnabled: false, + imageRegistry: 'docker.io', + imageRepository: 'freeleaps', + imageName: 'ailab', + imageBuilder: 'dind', + dockerfilePath: 'Dockerfile', + imageBuildRoot: '.', + imageReleaseArchitectures: ['linux/amd64', 'linux/arm64/v8'], + registryCredentialsId: 'freeleaps-devops-docker-hub-credentials', + semanticReleaseEnabled: true + ] + ] +} \ No newline at end of file diff --git a/aml-services/helm-pkg/ailab/Chart.yaml b/aml-services/helm-pkg/ailab/Chart.yaml new file mode 100644 index 00000000..4f527b6e --- /dev/null +++ b/aml-services/helm-pkg/ailab/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: ailab +description: A Helm Chart of ailab, which part of Freeleaps Platform, powered by Freeleaps. +type: application +version: 0.0.1 +appVersion: "0.0.1" diff --git a/aml-services/helm-pkg/ailab/templates/ailab/ailab-config.yaml b/aml-services/helm-pkg/ailab/templates/ailab/ailab-config.yaml new file mode 100644 index 00000000..f97a7c30 --- /dev/null +++ b/aml-services/helm-pkg/ailab/templates/ailab/ailab-config.yaml @@ -0,0 +1,21 @@ +apiVersion: v1 +kind: Secret +metadata: + name: ailab-config + namespace: {{ .Release.Namespace }} +type: Opaque +data: + SERVICE_API_ACCESS_HOST: {{ .Values.ailab.configs.serviceApiAccessHost | b64enc | quote }} + SERVICE_API_ACCESS_PORT: {{ .Values.ailab.configs.serviceApiAccessPort | toString | b64enc }} + CONTAINER_APP_ROOT: {{ .Values.ailab.configs.containerAppRoot | b64enc | quote }} + AZURE_TRANSLATION_API_KEY: {{ .Values.ailab.configs.azureTranslationApiKey | b64enc | quote }} + AZURE_TRANSLATION_API_LOCATION: {{ .Values.ailab.configs.azureTranslationApiLocation | b64enc | quote }} + AZURE_TRANSLATION_API_ENDPOINT: {{ .Values.ailab.configs.azureTranslationApiEndpoint | b64enc | quote }} + AZURE_DOCUMENT_TRANSLATION_API_ENDPOINT: {{ .Values.ailab.configs.azureDocumentTranslationApiEndpoint | b64enc | quote }} + AZURE_OPENAI_API_VERSION: {{ .Values.ailab.configs.azureOpenaiApiVersion | b64enc | quote }} + AZURE_OPENAI_API_KEY: {{ .Values.ailab.configs.azureOpenaiApiKey | b64enc | quote }} + AZURE_OPENAI_ENDPOINT: {{ .Values.ailab.configs.azureOpenaiEndpoint | b64enc | quote }} + ANTHROPIC_API_KEY: {{ .Values.ailab.configs.anthropicApiKey | b64enc | quote }} + ANTHROPIC_API_MODEL_VERSION: {{ .Values.ailab.configs.anthropicApiModelVersion | b64enc | quote }} + ANTHROPIC_API_MODEL_MAX_TOKEN: {{ .Values.ailab.configs.anthropicApiModelMaxToken | toString | b64enc }} + REDIS_URL: {{ .Values.ailab.configs.redisUrl | b64enc | quote }} \ No newline at end of file diff --git a/aml-services/helm-pkg/ailab/templates/ailab/certificate.yaml b/aml-services/helm-pkg/ailab/templates/ailab/certificate.yaml new file mode 100644 index 00000000..af4589d1 --- /dev/null +++ b/aml-services/helm-pkg/ailab/templates/ailab/certificate.yaml @@ -0,0 +1,27 @@ +{{ $namespace := .Release.Namespace }} +{{ $appVersion := .Chart.AppVersion | quote }} +{{ $releaseCertificate := .Release.Service }} +{{ $releaseName := .Release.Name }} +{{- range $ingress := .Values.ailab.ingresses }} +{{- if not $ingress.tls.exists }} +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: {{ $ingress.name }} + namespace: {{ $namespace }} + labels: + app.kubernetes.io/version: {{ $appVersion }} + app.kubernetes.io/name: {{ $ingress.name | quote }} + app.kubernetes.io/managed-by: {{ $releaseCertificate }} + app.kubernetes.io/instance: {{ $releaseName }} +spec: + commonName: {{ $ingress.host }} + dnsNames: + - {{ $ingress.host }} + issuerRef: + name: {{ $ingress.tls.issuerRef.name }} + kind: {{ $ingress.tls.issuerRef.kind }} + secretName: {{ $ingress.tls.name }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/aml-services/helm-pkg/ailab/templates/ailab/deployment.yaml b/aml-services/helm-pkg/ailab/templates/ailab/deployment.yaml new file mode 100644 index 00000000..d774c455 --- /dev/null +++ b/aml-services/helm-pkg/ailab/templates/ailab/deployment.yaml @@ -0,0 +1,97 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} + app.kubernetes.io/name: "ailab" + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/instance: {{ .Release.Name }} + name: "ailab" + namespace: {{ .Release.Namespace | quote }} +spec: + selector: + matchLabels: + app.kubernetes.io/name: "ailab" + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + replicas: {{ .Values.ailab.replicas }} + template: + metadata: + labels: + app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} + app.kubernetes.io/name: "ailab" + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/instance: {{ .Release.Name }} + annotations: + app.kubernetes.io/config-checksum: {{ include (print $.Template.BasePath "/ailab/ailab-config.yaml") . | sha256sum }} + spec: + containers: + - name: "ailab" + image: "{{ coalesce .Values.ailab.image.registry .Values.global.registry "docker.io"}}/{{ coalesce .Values.ailab.image.repository .Values.global.repository }}/{{ .Values.ailab.image.name }}:{{ .Values.ailab.image.tag | default "latest" }}" + imagePullPolicy: {{ .Values.ailab.image.imagePullPolicy | default "IfNotPresent" }} + ports: + {{- range $port := .Values.ailab.ports }} + - containerPort: {{ $port.containerPort }} + name: {{ $port.name }} + protocol: {{ $port.protocol }} + {{- end }} + {{- if .Values.ailab.resources }} + resources: + {{- toYaml .Values.ailab.resources | nindent 12 }} + {{- end }} + {{- if .Values.ailab.probes }} + {{- if and (.Values.ailab.probes.liveness) (eq .Values.ailab.probes.liveness.type "httpGet") }} + livenessProbe: + httpGet: + path: {{ .Values.ailab.probes.liveness.config.path }} + port: {{ .Values.ailab.probes.liveness.config.port }} + {{- if .Values.ailab.probes.liveness.config.initialDelaySeconds }} + initialDelaySeconds: {{ .Values.ailab.probes.liveness.config.initialDelaySeconds }} + {{- end }} + {{- if .Values.ailab.probes.liveness.config.periodSeconds }} + periodSeconds: {{ .Values.ailab.probes.liveness.config.periodSeconds }} + {{- end }} + {{- if .Values.ailab.probes.liveness.config.timeoutSeconds }} + timeoutSeconds: {{ .Values.ailab.probes.liveness.config.timeoutSeconds }} + {{- end }} + {{- if .Values.ailab.probes.liveness.config.successThreshold }} + successThreshold: {{ .Values.ailab.probes.liveness.config.successThreshold }} + {{- end }} + {{- if .Values.ailab.probes.liveness.config.failureThreshold }} + failureThreshold: {{ .Values.ailab.probes.liveness.config.failureThreshold }} + {{- end }} + {{- if .Values.ailab.probes.liveness.config.terminationGracePeriodSeconds }} + terminationGracePeriodSeconds: {{ .Values.ailab.probes.liveness.config.terminationGracePeriodSeconds }} + {{- end }} + {{- end }} + {{- if and (.Values.ailab.probes.readiness) (eq .Values.ailab.probes.readiness.type "httpGet") }} + readinessProbe: + httpGet: + path: {{ .Values.ailab.probes.readiness.config.path }} + port: {{ .Values.ailab.probes.readiness.config.port }} + {{- if .Values.ailab.probes.readiness.config.initialDelaySeconds }} + initialDelaySeconds: {{ .Values.ailab.probes.readiness.config.initialDelaySeconds }} + {{- end }} + {{- if .Values.ailab.probes.readiness.config.periodSeconds }} + periodSeconds: {{ .Values.ailab.probes.readiness.config.periodSeconds }} + {{- end }} + {{- if .Values.ailab.probes.readiness.config.timeoutSeconds }} + timeoutSeconds: {{ .Values.ailab.probes.readiness.config.timeoutSeconds }} + {{- end }} + {{- if .Values.ailab.probes.readiness.config.successThreshold }} + successThreshold: {{ .Values.ailab.probes.readiness.config.successThreshold }} + {{- end }} + {{- if .Values.ailab.probes.readiness.config.failureThreshold }} + failureThreshold: {{ .Values.ailab.probes.readiness.config.failureThreshold }} + {{- end }} + {{- end }} + {{- end}} + env: + {{- range $key, $value := .Values.ailab.configs }} + - name: {{ $key | snakecase | upper }} + valueFrom: + secretKeyRef: + name: ailab-config + key: {{ $key | snakecase | upper }} + {{- end }} \ No newline at end of file diff --git a/aml-services/helm-pkg/ailab/templates/ailab/ingress.yaml b/aml-services/helm-pkg/ailab/templates/ailab/ingress.yaml new file mode 100644 index 00000000..d56b1ebf --- /dev/null +++ b/aml-services/helm-pkg/ailab/templates/ailab/ingress.yaml @@ -0,0 +1,36 @@ +{{ $namespace := .Release.Namespace }} +{{ $appVersion := .Chart.AppVersion | quote }} +{{ $releaseIngress := .Release.Service }} +{{ $releaseName := .Release.Name }} +{{- range $ingress := .Values.ailab.ingresses }} +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ $ingress.name }} + namespace: {{ $namespace }} + labels: + app.kubernetes.io/version: {{ $appVersion }} + app.kubernetes.io/name: {{ $ingress.name | quote }} + app.kubernetes.io/managed-by: {{ $releaseIngress }} + app.kubernetes.io/instance: {{ $releaseName }} +spec: +{{- if $ingress.class }} + ingressClassName: {{ $ingress.class }} +{{- end }} +{{- if $ingress.tls }} + tls: + - hosts: + - {{ $ingress.host }} +{{- if $ingress.tls.exists }} + secretName: {{ $ingress.tls.secretRef.name }} +{{- else }} + secretName: {{ $ingress.tls.name }} +{{- end }} +{{- end }} + rules: + - host: {{ $ingress.host }} + http: + paths: +{{- toYaml $ingress.rules | nindent 10 }} +{{- end }} \ No newline at end of file diff --git a/aml-services/helm-pkg/ailab/templates/ailab/service.yaml b/aml-services/helm-pkg/ailab/templates/ailab/service.yaml new file mode 100644 index 00000000..abe67045 --- /dev/null +++ b/aml-services/helm-pkg/ailab/templates/ailab/service.yaml @@ -0,0 +1,26 @@ +{{ $namespace := .Release.Namespace }} +{{ $appVersion := .Chart.AppVersion | quote }} +{{ $releaseService := .Release.Service }} +{{ $releaseName := .Release.Name }} +{{- range $service := .Values.ailab.services }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ $service.name }} + namespace: {{ $namespace }} + labels: + app.kubernetes.io/version: {{ $appVersion }} + app.kubernetes.io/name: {{ $service.name | quote }} + app.kubernetes.io/managed-by: {{ $releaseService }} + app.kubernetes.io/instance: {{ $releaseName }} +spec: + ports: + - port: {{ $service.port }} + targetPort: {{ $service.targetPort }} + selector: + app.kubernetes.io/version: {{ $appVersion }} + app.kubernetes.io/name: "ailab" + app.kubernetes.io/managed-by: {{ $releaseService }} + app.kubernetes.io/instance: {{ $releaseName }} +{{- end }} \ No newline at end of file diff --git a/aml-services/helm-pkg/ailab/templates/ailab/servicemonitor.yaml b/aml-services/helm-pkg/ailab/templates/ailab/servicemonitor.yaml new file mode 100644 index 00000000..88318709 --- /dev/null +++ b/aml-services/helm-pkg/ailab/templates/ailab/servicemonitor.yaml @@ -0,0 +1,40 @@ +{{ $namespace := .Release.Namespace }} +{{ $appVersion := .Chart.AppVersion | quote }} +{{ $releaseService := .Release.Service }} +{{ $releaseName := .Release.Name }} + +{{- range $service := .Values.ailab.services }} +{{- if $service.serviceMonitor.enabled }} +--- +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ $service.name }}-monitor + namespace: {{ $service.serviceMonitor.namespace }} + labels: + app.kubernetes.io/version: {{ $appVersion }} + app.kubernetes.io/name: {{ $service.name }}-monitor + app.kubernetes.io/managed-by: {{ $releaseService }} + app.kubernetes.io/instance: {{ $releaseName }} + {{- if $service.serviceMonitor.labels }} + {{- toYaml $service.serviceMonitor.labels | nindent 4 }} + {{- end }} +spec: + endpoints: + - path: /api/_/metrics + targetPort: {{ $service.targetPort }} + {{- if $service.serviceMonitor.interval }} + interval: {{ $service.serviceMonitor.interval }} + {{- end }} + {{- if $service.serviceMonitor.scrapeTimeout }} + scrapeTimeout: {{ $service.serviceMonitor.scrapeTimeout }} + {{- end }} + namespaceSelector: + matchNames: + - {{ $namespace | quote }} + selector: + matchLabels: + app.kubernetes.io/name: {{ $service.name }} + app.kubernetes.io/instance: {{ $releaseName }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/aml-services/helm-pkg/ailab/templates/ailab/vpa.yaml b/aml-services/helm-pkg/ailab/templates/ailab/vpa.yaml new file mode 100644 index 00000000..bb7f7ac1 --- /dev/null +++ b/aml-services/helm-pkg/ailab/templates/ailab/vpa.yaml @@ -0,0 +1,32 @@ +{{- if .Values.ailab.vpa }} +--- +apiVersion: autoscaling.k8s.io/v1 +kind: VerticalPodAutoscaler +metadata: + name: {{ .Release.Name }}-vpa + namespace: {{ .Release.Namespace }} +spec: + targetRef: + apiVersion: apps/v1 + kind: Deployment + name: ailab + resourcePolicy: + containerPolicies: + - containerName: '*' + {{- if .Values.ailab.vpa.minAllowed.enabled }} + minAllowed: + cpu: {{ .Values.ailab.vpa.minAllowed.cpu }} + memory: {{ .Values.ailab.vpa.minAllowed.memory }} + {{- end }} + {{- if .Values.ailab.vpa.maxAllowed.enabled }} + maxAllowed: + cpu: {{ .Values.ailab.vpa.maxAllowed.cpu }} + memory: {{ .Values.ailab.vpa.maxAllowed.memory }} + {{- end }} + {{- if .Values.ailab.vpa.controlledResources }} + controlledResources: + {{- range .Values.ailab.vpa.controlledResources }} + - {{ . }} + {{- end }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/aml-services/helm-pkg/ailab/values.alpha.yaml b/aml-services/helm-pkg/ailab/values.alpha.yaml new file mode 100644 index 00000000..e1d2bdae --- /dev/null +++ b/aml-services/helm-pkg/ailab/values.alpha.yaml @@ -0,0 +1,65 @@ +global: + registry: docker.io + repository: freeleaps + nodeSelector: {} +ailab: + replicas: 1 + image: + registry: + repository: freeleaps + name: ailab + tag: 0.0.1 + imagePullPolicy: IfNotPresent + ports: + - name: http + containerPort: 8009 + protocol: TCP + resources: + requests: + cpu: "0.1" + memory: "64Mi" + limits: + cpu: "0.2" + memory: "128Mi" + probes: {} + services: + - name: ailab-service + type: ClusterIP + port: 8009 + targetPort: 8009 + serviceMonitor: + enabled: false + labels: + release: kube-prometheus-stack + namespace: freeleaps-monitoring-system + interval: 30s + scrapeTimeout: "" + # Defaults to {}, which means doesn't have any ingress + ingresses: {} + configs: + serviceApiAccessHost: "0.0.0.0" + serviceApiAccessPort: 8009 + containerAppRoot: /app + azureTranslationApiKey: "eaf4c14eb06b4cd790e90111dc6e9f57" + azureTranslationApiLocation: "global" + azureTranslationApiEndpoint: "https://api.cognitive.microsofttranslator.com" + azureDocumentTranslationApiEndpoint: "https://freeleaps-translation.cognitiveservices.azure.com/" + azureOpenaiApiVersion: "2024-08-06" + azureOpenaiApiKey: "BZWqFEpDiMFMAdJvGKeDisjwepqEQpKDH8R9R8Yjp8GFXCRqjeuzJQQJ99ALAC4f1cMXJ3w3AAABACOGrMqX" + azureOpenaiEndpoint: "https://freeleaps-azure-openai.openai.azure.com/" + anthropicApiKey: "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" + anthropicApiModelVersion: "claude-3-5-sonnet-20241022" + anthropicApiModelMaxToken: 1024 + redisUrl: "redis://freeleaps-alpha-redis-master.freeleaps-alpha.svc.freeleaps.cluster:6379" + vpa: + minAllowed: + enabled: false + cpu: 100m + memory: 64Mi + maxAllowed: + enabled: true + cpu: 200m + memory: 128Mi + controlledResources: + - cpu + - memory diff --git a/aml-services/helm-pkg/ailab/values.yaml b/aml-services/helm-pkg/ailab/values.yaml new file mode 100644 index 00000000..e1d2bdae --- /dev/null +++ b/aml-services/helm-pkg/ailab/values.yaml @@ -0,0 +1,65 @@ +global: + registry: docker.io + repository: freeleaps + nodeSelector: {} +ailab: + replicas: 1 + image: + registry: + repository: freeleaps + name: ailab + tag: 0.0.1 + imagePullPolicy: IfNotPresent + ports: + - name: http + containerPort: 8009 + protocol: TCP + resources: + requests: + cpu: "0.1" + memory: "64Mi" + limits: + cpu: "0.2" + memory: "128Mi" + probes: {} + services: + - name: ailab-service + type: ClusterIP + port: 8009 + targetPort: 8009 + serviceMonitor: + enabled: false + labels: + release: kube-prometheus-stack + namespace: freeleaps-monitoring-system + interval: 30s + scrapeTimeout: "" + # Defaults to {}, which means doesn't have any ingress + ingresses: {} + configs: + serviceApiAccessHost: "0.0.0.0" + serviceApiAccessPort: 8009 + containerAppRoot: /app + azureTranslationApiKey: "eaf4c14eb06b4cd790e90111dc6e9f57" + azureTranslationApiLocation: "global" + azureTranslationApiEndpoint: "https://api.cognitive.microsofttranslator.com" + azureDocumentTranslationApiEndpoint: "https://freeleaps-translation.cognitiveservices.azure.com/" + azureOpenaiApiVersion: "2024-08-06" + azureOpenaiApiKey: "BZWqFEpDiMFMAdJvGKeDisjwepqEQpKDH8R9R8Yjp8GFXCRqjeuzJQQJ99ALAC4f1cMXJ3w3AAABACOGrMqX" + azureOpenaiEndpoint: "https://freeleaps-azure-openai.openai.azure.com/" + anthropicApiKey: "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" + anthropicApiModelVersion: "claude-3-5-sonnet-20241022" + anthropicApiModelMaxToken: 1024 + redisUrl: "redis://freeleaps-alpha-redis-master.freeleaps-alpha.svc.freeleaps.cluster:6379" + vpa: + minAllowed: + enabled: false + cpu: 100m + memory: 64Mi + maxAllowed: + enabled: true + cpu: 200m + memory: 128Mi + controlledResources: + - cpu + - memory diff --git a/aml-services/prod/.gitkeep b/aml-services/prod/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/diagrams/workflow.drawio b/diagrams/workflow.drawio new file mode 100644 index 00000000..56eb9ad5 --- /dev/null +++ b/diagrams/workflow.drawio @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file