Skip to content

Commit 63f0787

Browse files
committed
Add AI to Sippy
1 parent 1789005 commit 63f0787

File tree

196 files changed

+43167
-651
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

196 files changed

+43167
-651
lines changed

cmd/sippy/component_readiness.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ func (f *ComponentReadinessFlags) runServerMode() error {
188188
f.ComponentReadinessFlags.CRTimeRoundingFactor,
189189
views,
190190
config,
191+
nil, // No AI use yet in Component Readiness
191192
)
192193

193194
if f.MetricsAddr != "" {

cmd/sippy/serve.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/spf13/pflag"
1515

1616
resources "github.com/openshift/sippy"
17+
"github.com/openshift/sippy/pkg/ai"
1718
"github.com/openshift/sippy/pkg/apis/cache"
1819
"github.com/openshift/sippy/pkg/bigquery"
1920
"github.com/openshift/sippy/pkg/dataloader/prowloader/gcs"
@@ -26,6 +27,7 @@ import (
2627
)
2728

2829
type ServerFlags struct {
30+
AIFlags *flags.AIFlags
2931
BigQueryFlags *flags.BigQueryFlags
3032
CacheFlags *flags.CacheFlags
3133
DBFlags *flags.PostgresFlags
@@ -40,6 +42,7 @@ type ServerFlags struct {
4042

4143
func NewServerFlags() *ServerFlags {
4244
return &ServerFlags{
45+
AIFlags: flags.NewAIFlags(),
4346
BigQueryFlags: flags.NewBigQueryFlags(),
4447
CacheFlags: flags.NewCacheFlags(),
4548
DBFlags: flags.NewPostgresDatabaseFlags(),
@@ -53,6 +56,7 @@ func NewServerFlags() *ServerFlags {
5356
}
5457

5558
func (f *ServerFlags) BindFlags(flagSet *pflag.FlagSet) {
59+
f.AIFlags.BindFlags(flagSet)
5660
f.BigQueryFlags.BindFlags(flagSet)
5761
f.CacheFlags.BindFlags(flagSet)
5862
f.DBFlags.BindFlags(flagSet)
@@ -117,6 +121,11 @@ func NewServeCommand() *cobra.Command {
117121
}
118122
}
119123

124+
var llmClient *ai.LLMClient
125+
if f.AIFlags.Endpoint != "" {
126+
llmClient = f.AIFlags.GetLLMClient()
127+
}
128+
120129
// Make sure the db is intialized, otherwise let the user know:
121130
prowJobs := []models.ProwJob{}
122131
res := dbc.DB.Find(&prowJobs).Limit(1)
@@ -153,6 +162,7 @@ func NewServeCommand() *cobra.Command {
153162
f.ComponentReadinessFlags.CRTimeRoundingFactor,
154163
views,
155164
config,
165+
llmClient,
156166
)
157167

158168
if f.MetricsAddr != "" {

go.mod

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ require (
2020
github.com/jferrl/go-githubauth v1.1.0
2121
github.com/lib/pq v1.10.9
2222
github.com/montanaflynn/stats v0.6.6
23+
github.com/openai/openai-go v0.1.0-beta.2
2324
github.com/openshift-eng/ci-test-mapping v0.0.0-20231030141615-24a18ed8fe3a
2425
github.com/pkg/errors v0.9.1
2526
github.com/prometheus/client_golang v1.20.3
@@ -29,14 +30,15 @@ require (
2930
github.com/spf13/pflag v1.0.5
3031
github.com/stretchr/testify v1.9.0
3132
github.com/tcnksm/go-gitconfig v0.1.2
32-
github.com/tidwall/gjson v1.14.2
33+
github.com/tidwall/gjson v1.14.4
3334
github.com/trivago/tgo v1.0.7
3435
golang.org/x/oauth2 v0.23.0
3536
google.golang.org/api v0.191.0
3637
gopkg.in/redis.v5 v5.2.9
3738
gopkg.in/yaml.v3 v3.0.1
3839
gorm.io/driver/postgres v1.2.1
3940
gorm.io/gorm v1.22.2
41+
k8s.io/apimachinery v0.30.1
4042
sigs.k8s.io/prow v0.0.0-20241203114859-f9126b38a431
4143
)
4244

@@ -163,7 +165,8 @@ require (
163165
github.com/stoewer/go-strcase v1.2.0 // indirect
164166
github.com/tektoncd/pipeline v0.61.0 // indirect
165167
github.com/tidwall/match v1.1.1 // indirect
166-
github.com/tidwall/pretty v1.2.0 // indirect
168+
github.com/tidwall/pretty v1.2.1 // indirect
169+
github.com/tidwall/sjson v1.2.5 // indirect
167170
github.com/xanzy/ssh-agent v0.3.3 // indirect
168171
github.com/zeebo/xxh3 v1.0.2 // indirect
169172
go.opencensus.io v0.24.0 // indirect
@@ -176,14 +179,14 @@ require (
176179
go.uber.org/zap v1.27.0 // indirect
177180
go4.org v0.0.0-20201209231011-d4a079459e60 // indirect
178181
gocloud.dev v0.40.0 // indirect
179-
golang.org/x/crypto v0.27.0 // indirect
182+
golang.org/x/crypto v0.32.0 // indirect
180183
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e // indirect
181184
golang.org/x/mod v0.20.0 // indirect
182-
golang.org/x/net v0.28.0 // indirect
183-
golang.org/x/sync v0.8.0 // indirect
184-
golang.org/x/sys v0.25.0 // indirect
185-
golang.org/x/term v0.24.0 // indirect
186-
golang.org/x/text v0.18.0 // indirect
185+
golang.org/x/net v0.34.0 // indirect
186+
golang.org/x/sync v0.10.0 // indirect
187+
golang.org/x/sys v0.29.0 // indirect
188+
golang.org/x/term v0.28.0 // indirect
189+
golang.org/x/text v0.21.0 // indirect
187190
golang.org/x/time v0.6.0 // indirect
188191
golang.org/x/tools v0.24.0 // indirect
189192
golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 // indirect
@@ -198,7 +201,6 @@ require (
198201
gopkg.in/warnings.v0 v0.1.2 // indirect
199202
gopkg.in/yaml.v2 v2.4.0 // indirect
200203
k8s.io/api v0.30.1 // indirect
201-
k8s.io/apimachinery v0.30.1 // indirect
202204
k8s.io/client-go v0.30.1 // indirect
203205
k8s.io/klog/v2 v2.120.1 // indirect
204206
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect

go.sum

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
573573
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
574574
github.com/onsi/gomega v1.32.0 h1:JRYU78fJ1LPxlckP6Txi/EYqJvjtMrDC04/MM5XRHPk=
575575
github.com/onsi/gomega v1.32.0/go.mod h1:a4x4gW6Pz2yK1MAmvluYme5lvYTn61afQ2ETw/8n4Lg=
576+
github.com/openai/openai-go v0.1.0-beta.2 h1:Ra5nCFkbEl9w+UJwAciC4kqnIBUCcJazhmMA0/YN894=
577+
github.com/openai/openai-go v0.1.0-beta.2/go.mod h1:g461MYGXEXBVdV5SaR/5tNzNbSfwTBBefwc+LlDCK0Y=
576578
github.com/openshift-eng/ci-test-mapping v0.0.0-20231030141615-24a18ed8fe3a h1:bH+5JOkdlBENYZo6OaTA3ra2RjJsFFK+upv5CUAL6mM=
577579
github.com/openshift-eng/ci-test-mapping v0.0.0-20231030141615-24a18ed8fe3a/go.mod h1:HtbWQQG60/CJDMXoRkRvcdR2WJniLk4osp2kUCW4Q3E=
578580
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
@@ -683,12 +685,16 @@ github.com/tcnksm/go-gitconfig v0.1.2 h1:iiDhRitByXAEyjgBqsKi9QU4o2TNtv9kPP3RgPg
683685
github.com/tcnksm/go-gitconfig v0.1.2/go.mod h1:/8EhP4H7oJZdIPyT+/UIsG87kTzrzM4UsLGSItWYCpE=
684686
github.com/tektoncd/pipeline v0.61.0 h1:w1XBPFc8Sh/DIcBPRL/ndWtbZZl12W3zpkm4JSDL1gU=
685687
github.com/tektoncd/pipeline v0.61.0/go.mod h1:m2zG2B124Gh7/VB4G3+NGSyyzy0q5ceNyLUqIz0cIyQ=
686-
github.com/tidwall/gjson v1.14.2 h1:6BBkirS0rAHjumnjHF6qgy5d2YAJ1TLIaFE2lzfOLqo=
687688
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
689+
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
690+
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
688691
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
689692
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
690-
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
691693
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
694+
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
695+
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
696+
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
697+
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
692698
github.com/trivago/tgo v1.0.7 h1:uaWH/XIy9aWYWpjm2CU3RpcqZXmX2ysQ9/Go+d9gyrM=
693699
github.com/trivago/tgo v1.0.7/go.mod h1:w4dpD+3tzNIIiIfkWWa85w5/B77tlvdZckQ+6PkFnhc=
694700
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
@@ -763,8 +769,8 @@ golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2Uz
763769
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
764770
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
765771
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
766-
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
767-
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
772+
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
773+
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
768774
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
769775
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
770776
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -856,8 +862,8 @@ golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
856862
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
857863
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
858864
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
859-
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
860-
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
865+
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
866+
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
861867
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
862868
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
863869
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -886,8 +892,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ
886892
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
887893
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
888894
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
889-
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
890-
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
895+
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
896+
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
891897
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
892898
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
893899
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -954,8 +960,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
954960
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
955961
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
956962
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
957-
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
958-
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
963+
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
964+
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
959965
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
960966
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
961967
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -966,8 +972,8 @@ golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
966972
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
967973
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
968974
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
969-
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
970-
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
975+
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
976+
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
971977
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
972978
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
973979
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -982,8 +988,8 @@ golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
982988
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
983989
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
984990
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
985-
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
986-
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
991+
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
992+
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
987993
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
988994
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
989995
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=

pkg/ai/README.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Sippy AI
2+
3+
This package contains tools for working with Generative AI. An example is provided
4+
with job runs, which uses a system prompt to prime the AI with instructions about how
5+
it should operate. In this case, it's provided some data about a job run like it's
6+
overall result and failed tests, and instructed to provide a summary.
7+
8+
The data provided to the AI is limited in that you wouldn't be able to provide a whole
9+
build log, but you can provide relevant snippets. For example, it would be a good
10+
idea to expand the job run data to include basic information about machines, nodes, and
11+
cluster operators.
12+
13+
Sippy AI can be used with any OpenAI-compatible API. For local development, you can
14+
consider Ollama or vLLM.
15+
16+
If you need to specify an auth key, set the environment variable `OPENAI_API_KEY`.
17+
18+
## Example usage
19+
20+
Ollama example. Install and launch Ollama:
21+
22+
```
23+
ollama serve
24+
```
25+
26+
Then simply launch Sippy, specifying the API endpoint and model.
27+
28+
```
29+
./sippy serve --mode ocp \
30+
--listen-metrics="" \
31+
--log-level=debug \
32+
--ai-endpoint=http://127.0.0.1:11434/v1/ \
33+
--ai-model mistral
34+
```
35+
36+
### Fetching job run summary
37+
38+
```
39+
$ curl localhost:8080/api/ai/job_run?prow_job_run_id=1904667807946117120
40+
The CI job, "periodic-ci-openshift-hypershift-release-4.19-periodics-e2e-aws-ovn-conformance," failed due to various
41+
test failures, including DNS resolution issues for Prometheus and Thanos services. This resulted in multiple test
42+
cases failing, contributing to the overall job failure.
43+
```
44+
45+
```
46+
$ curl localhost:8080/api/ai/job_run?prow_job_run_id=1904793689171955712
47+
The CI job 'periodic-ci-openshift-release-master-nightly-4.19-e2e-aws-ovn-fips' successfully
48+
completed with no test failures.
49+
```
50+
51+
## How else could I use this?
52+
53+
Some other ideas on how to use generative AI with Sippy:
54+
55+
- Generate a bug title/description from a failed test
56+
- Examine all failed jobs on a payload, and ask the AI to find common themes
57+
- Examine a single test failure, and ask the AI to provide debugging steps, and a summary of what went wrong
58+

pkg/ai/job_runs.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package ai
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"strings"
7+
"time"
8+
9+
log "github.com/sirupsen/logrus"
10+
11+
jobQueries "github.com/openshift/sippy/pkg/api"
12+
v1 "github.com/openshift/sippy/pkg/apis/sippyprocessing/v1"
13+
"github.com/openshift/sippy/pkg/db"
14+
)
15+
16+
// Prompt to use for jobRunAnalysis.
17+
// TODO: Look into GCS bucket for node, machine, and operator info -- and whatever other small bits of data we can get.
18+
const jobRunAnalysisPrompt = `You are an expert in analyzing OpenShift CI job failures. You will receive
19+
structured JSON input containing:
20+
21+
- The CI job name
22+
- A high-level overall result for the job (e.g., success, install, test failure, other)
23+
- A list of test failures and their output snippets
24+
25+
Based on this, return a concise summary (1–3 sentences max) explaining why the job failed. Be clear, factual, and avoid
26+
speculation. Be brief.`
27+
28+
type jobRunData struct {
29+
Name string
30+
Reason string
31+
TestFailures map[string]string
32+
}
33+
34+
func AnalyzeJobRun(ctx context.Context, llmClient *LLMClient, dbc *db.DB, jobRunID int64) (string, error) {
35+
jLog := log.WithField("JobRunID", jobRunID)
36+
dbStart := time.Now()
37+
jLog.Info("Querying DB for job run data")
38+
jr, err := jobQueries.FetchJobRun(dbc, jobRunID, false, []string{"Tests.ProwJobRunTestOutput"}, jLog)
39+
if err != nil {
40+
return "", err
41+
}
42+
jLog.Infof("DB query complete after %+v", time.Since(dbStart))
43+
44+
failures := make(map[string]string)
45+
for _, test := range jr.Tests {
46+
// skip synthetic tests
47+
if strings.Contains(test.Test.Name, "sig-sippy") {
48+
continue
49+
}
50+
51+
if v1.TestStatus(test.Status) == v1.TestStatusFailure {
52+
output := test.ProwJobRunTestOutput.Output
53+
// some tests are very chatty, get the last 256 characters where
54+
// the meat of the failure probably is.
55+
if len(output) > 256 {
56+
output = output[len(output)-256:]
57+
}
58+
failures[test.Test.Name] = output
59+
}
60+
}
61+
62+
jrData := jobRunData{
63+
Name: jr.ProwJob.Name,
64+
Reason: jr.OverallResult.String(),
65+
TestFailures: failures,
66+
}
67+
68+
jobRunSummary, err := json.MarshalIndent(jrData, "", " ")
69+
if err != nil {
70+
return "", err
71+
}
72+
73+
llmStart := time.Now()
74+
jLog = jLog.WithField("Name", jrData.Name)
75+
jLog.Info("Asking LLM for job run summary")
76+
res, err := llmClient.Chat(ctx, jobRunAnalysisPrompt, string(jobRunSummary))
77+
if err == nil {
78+
jLog.Infof("LLM complete in %+v", time.Since(llmStart))
79+
}
80+
81+
return res, err
82+
}

0 commit comments

Comments
 (0)