Skip to content

Commit

Permalink
feat: add ai suggestions in UI
Browse files Browse the repository at this point in the history
  • Loading branch information
michael12312 committed Feb 28, 2024
1 parent 8846abd commit 1e9c5ab
Show file tree
Hide file tree
Showing 12 changed files with 125 additions and 11 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
echo "REPO=theliv" >> $GITHUB_ENV
echo "using ${{ env.VERSION }} as the release version"
- name: Login to GitHub Container Registry
if: github.event_name == 'push'
if: github.event_name == 'pull_request'
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
Expand All @@ -41,5 +41,5 @@ jobs:
with:
context: .
file: ./build/Dockerfile
push: ${{ github.event_name == 'push' }}
push: ${{ github.event_name == 'pull_request' }}
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }}
10 changes: 10 additions & 0 deletions internal/investigators/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package investigators
import (
"bytes"
"context"
"fmt"
"regexp"
"strings"
"sync"
Expand All @@ -16,6 +17,7 @@ import (

"go.uber.org/zap"

"github.com/fidelity/theliv/pkg/ai"
log "github.com/fidelity/theliv/pkg/log"
"github.com/fidelity/theliv/pkg/observability"
v1 "k8s.io/api/core/v1"
Expand Down Expand Up @@ -154,3 +156,11 @@ func appendSolution(problem *problem.Problem, solutions interface{}, commands in
}
}
}

func getAiSuggestion(issue string) string {
return fmt.Sprintf(ai.DefaultPrompt, issue)
}

func getAiKnowledge(issue string) string {
return fmt.Sprintf(ai.KnowledgePrompt, issue)
}
17 changes: 17 additions & 0 deletions internal/investigators/pod_pending_investigator.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ func PodNotRunningInvestigator(ctx context.Context, wg *sync.WaitGroup, problem
} else {
solutions, commands = getPendingPodUnknownSolution(ctx, pod)
}
var wg2 sync.WaitGroup
wg2.Add(2)
callAiSuggestion(ctx, &wg2, problem, input, "PodFailedScheduled, "+failSchedule)
callAiKnowledge(ctx, &wg2, problem, input, "PodFailedScheduled")
wg2.Wait()
} else if len(failMount) > 0 {
commands = appendSeq(commands, GetSolutionsByTemplate(ctx, KubeDescribePoCmd, pod, true)[0])
commands = appendSeq(commands, GetSolutionsByTemplate(ctx, GetEventsCmd, pod, true)[0])
Expand All @@ -148,6 +153,18 @@ func PodNotRunningInvestigator(ctx context.Context, wg *sync.WaitGroup, problem
appendSolution(problem, solutions, commands)
}

func callAiKnowledge(ctx context.Context, wg *sync.WaitGroup, problem *problem.Problem, input *problem.DetectorCreationInput, prompt string) {
defer wg.Done()
knowledge, _ := input.AiClient.GetCompletion(ctx, getAiKnowledge(prompt))
problem.AiKnowledge.Append(strings.Split(knowledge, "\n")...)
}

func callAiSuggestion(ctx context.Context, wg *sync.WaitGroup, problem *problem.Problem, input *problem.DetectorCreationInput, prompt string) {
defer wg.Done()
suggestions, _ := input.AiClient.GetCompletion(ctx, getAiSuggestion(prompt))
problem.AiSuggestions.Append(strings.Split(suggestions, "\n")...)
}

func appendPVSolution(ctx context.Context, po v1.Pod, solutions []string, solution string) ([]string, []string) {
addSolutions := GetSolutionsByTemplate(ctx, solution, po, true)
solutions = append(solutions, addSolutions...)
Expand Down
2 changes: 2 additions & 0 deletions internal/problem/aggregate.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ func getReportCardResource(ctx context.Context, p *Problem, resource ResourceDet
cr := createReportCardResource(ctx, p, resource.Resource.(metav1.Object), resource.ResourceKind)
cr.Issue.Solutions = append(cr.Issue.Solutions, p.SolutionDetails.GetStore()...)
cr.Issue.Commands = append(cr.Issue.Commands, p.UsefulCommands.GetStore()...)
cr.Issue.AiKnowledge = append(cr.Issue.AiKnowledge, p.AiKnowledge.GetStore()...)
cr.Issue.AiSuggestions = append(cr.Issue.AiSuggestions, p.AiSuggestions.GetStore()...)

// cr.Issue.Documents = urlToStr(p.Docs)
// if resource.Deeplink != nil {
Expand Down
2 changes: 2 additions & 0 deletions internal/problem/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"time"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/fidelity/theliv/pkg/ai"
"github.com/fidelity/theliv/pkg/kubeclient"
"github.com/fidelity/theliv/pkg/observability"
"k8s.io/client-go/rest"
Expand All @@ -28,4 +29,5 @@ type DetectorCreationInput struct {
AwsConfig aws.Config
KubeClient *kubeclient.KubeClient
EventRetriever observability.EventRetriever
AiClient ai.AzureAIClient
}
4 changes: 3 additions & 1 deletion internal/problem/problem.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ type Problem struct {
CauseLevel int
SolutionDetails *common.LockedSlice // output field after detetor. It contains solutions details to show in UI.
UsefulCommands *common.LockedSlice // output field after detetor. It contains solutions details to show in UI.
AffectedResources ResourceDetails // output field after detetor. It contains the resources affected by this problem that to show in UI.
AiSuggestions *common.LockedSlice
AiKnowledge *common.LockedSlice
AffectedResources ResourceDetails // output field after detetor. It contains the resources affected by this problem that to show in UI.
}

type ResourceDetails struct {
Expand Down
16 changes: 9 additions & 7 deletions internal/problem/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ type ReportCard struct {
}

type ReportCardIssue struct {
Name string `json:"name"`
Description string `json:"description"`
Solutions []string `json:"solutions,omitempty"`
Commands []string `json:"commands,omitempty"`
CreatedTime string `json:"createdTime,omitempty"`
Tags map[string]string `json:"tags,omitempty"`
DomainName DomainName `json:"domainName,omitempty"`
Name string `json:"name"`
Description string `json:"description"`
Solutions []string `json:"solutions,omitempty"`
Commands []string `json:"commands,omitempty"`
AiSuggestions []string
AiKnowledge []string
CreatedTime string `json:"createdTime,omitempty"`
Tags map[string]string `json:"tags,omitempty"`
DomainName DomainName `json:"domainName,omitempty"`
// Documents []string `json:"documents,omitempty"`
CauseLevel int `json:"causelevel,omitempty"`
}
Expand Down
15 changes: 15 additions & 0 deletions pkg/ai/prompts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Copyright FMR LLC <[email protected]>
*
* SPDX-License-Identifier: Apache
*/
package ai

const (
DefaultPrompt = `Simplify the following Kubernetes error message delimited by triple dashes written in English language; --- %s ---.
Provide the most possible solution in a step by step style in no more than 560 characters. Write the output in the following format:
Error: {Explain error here}
Solution: {Step by step solution here}
`
KnowledgePrompt = "In kubernetes, what can cause %s?"
)
12 changes: 12 additions & 0 deletions pkg/service/detector.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/fidelity/theliv/internal/investigators"
in "github.com/fidelity/theliv/internal/investigators"
"github.com/fidelity/theliv/internal/problem"
"github.com/fidelity/theliv/pkg/ai"
"github.com/fidelity/theliv/pkg/common"
com "github.com/fidelity/theliv/pkg/common"
"github.com/fidelity/theliv/pkg/config"
Expand Down Expand Up @@ -72,6 +73,15 @@ func DetectAlerts(ctx context.Context) (interface{}, error) {
eventRetriever := k8s.NewK8sEventRetriever(client)
input.EventRetriever = eventRetriever

token, err := ai.GetToken(ctx)
if err != nil {
return nil, err
}
aiConfig := GetAiConfig(token.AccessToken)
var azClient = ai.AzureAIClient{}
azClient.Configure(&aiConfig)
input.AiClient = azClient

alerts, err := prometheus.GetAlerts(ctx, input)
if err != nil {
return nil, theErr.NewCommonError(ctx, 6, com.PrometheusNotAvailable+contact)
Expand Down Expand Up @@ -121,6 +131,8 @@ func buildProblemsFromAlerts(alerts []v1.Alert) []*problem.Problem {
CauseLevel: 0,
SolutionDetails: common.InitLockedSlice(),
UsefulCommands: common.InitLockedSlice(),
AiSuggestions: common.InitLockedSlice(),
AiKnowledge: common.InitLockedSlice(),
AffectedResources: problem.ResourceDetails{},
}
p.Name = string(alert.Labels[model.LabelName("alertname")])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,24 @@ <h2>{{popupResource}}.yaml
<li *ngFor="let solution of resource.issue.solutions">{{solution}}</li>
</ul>
</div>
<div class="detail-msg-suggestion">
<div>
<fa-icon [icon]="faEnvelope"></fa-icon>AI Suggestions:
</div>
<p *ngIf="!resource.issue.AiSuggestions">No AI Suggestions</p>
<ul *ngIf="resource.issue.AiSuggestions">
<li *ngFor="let solution of resource.issue.AiSuggestions">{{solution}}</li>
</ul>
</div>
<div class="detail-msg-knowledge">
<div>
<fa-icon [icon]="faComment"></fa-icon>AI knowledge:
</div>
<p *ngIf="!resource.issue.AiKnowledge">No AI knowledge</p>
<ul *ngIf="resource.issue.AiKnowledge">
<li *ngFor="let solution of resource.issue.AiKnowledge">{{solution}}</li>
</ul>
</div>
<div class="detail-msg-commands">
<div>
<fa-icon [icon]="faEdit"></fa-icon>Useful Commands:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,38 @@ li {
// flex-shrink: 0;
// }
}
&-suggestion {
div {
font-weight: 600;
color: var(--alter);
}
li {
white-space: pre-line;
list-style: none;
color: var(--subtitle-color);
margin-top: -0.5em;
}
p {
margin-top: 0.2em;
margin-bottom: 1.2em;
}
}
&-knowledge {
div {
font-weight: 600;
color: var(--color-summary-3-highlight);
}
li {
white-space: pre-line;
list-style: none;
color: var(--subtitle-color);
margin-top: -0.5em;
}
p {
margin-top: 0.2em;
margin-bottom: 1.2em;
}
}
&-commands {
div {
font-weight: 600;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/
import { Component, OnInit } from '@angular/core';
import { faExclamationTriangle, faLightbulb, faTimes, faThumbsUp, faExternalLinkAlt,
faClipboard, faShareAlt, faCopy, faDownload, faBookReader, faArrowLeft, faEdit } from '@fortawesome/free-solid-svg-icons';
faClipboard, faShareAlt, faCopy, faDownload, faBookReader, faArrowLeft, faEdit, faEnvelope, faComment } from '@fortawesome/free-solid-svg-icons';
import { ActivatedRoute } from '@angular/router';
import { KubernetesService } from 'src/app/services/kubernetes.service';
import { SocialUtil } from '../../shared/util/social-util';
Expand Down Expand Up @@ -42,6 +42,8 @@ export class ResourceGroupContentComponent implements OnInit {
faClipboard = faClipboard;
faBookReader = faBookReader;
faArrowLeft = faArrowLeft;
faEnvelope = faEnvelope;
faComment = faComment;

ns: any;
cluster: any;
Expand Down

0 comments on commit 1e9c5ab

Please sign in to comment.