diff --git a/go.mod b/go.mod index 34566607..f9d28313 100644 --- a/go.mod +++ b/go.mod @@ -17,6 +17,7 @@ require ( github.com/qiniu/x v1.13.10 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.9.0 + github.com/tmc/langchaingo v0.1.12 github.com/xanzy/go-gitlab v0.109.0 golang.org/x/mod v0.21.0 k8s.io/api v0.28.3 @@ -38,10 +39,11 @@ require ( github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.19 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.17 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/containerd/log v0.1.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/distribution/reference v0.6.0 // indirect + github.com/dlclark/regexp2 v1.10.0 // indirect github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect @@ -50,10 +52,10 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect - github.com/go-openapi/swag v0.22.3 // indirect + github.com/go-openapi/swag v0.22.4 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-github/v56 v56.0.0 // indirect @@ -72,13 +74,12 @@ require ( github.com/moby/sys/sequential v0.6.0 // indirect github.com/moby/sys/user v0.3.0 // indirect github.com/moby/sys/userns v0.1.0 // indirect - github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/morikuni/aec v1.0.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/pkoukk/tiktoken-go v0.1.6 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.54.0 // indirect @@ -92,12 +93,12 @@ require ( go.opentelemetry.io/otel/sdk v1.29.0 // indirect go.opentelemetry.io/otel/trace v1.29.0 // indirect golang.org/x/net v0.28.0 // indirect - golang.org/x/oauth2 v0.19.0 // indirect + golang.org/x/oauth2 v0.20.0 // indirect golang.org/x/sync v0.8.0 // indirect golang.org/x/sys v0.25.0 // indirect golang.org/x/term v0.23.0 // indirect golang.org/x/text v0.17.0 // indirect - golang.org/x/time v0.3.0 // indirect + golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/go.sum b/go.sum index af0927d2..c3c6b8ef 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Carlji/prow v1.0.0 h1:URdiAovXck7M+xO/B9+3idKBy8LeFGsifZyDuprbRmE= github.com/Carlji/prow v1.0.0/go.mod h1:ET3UlJAy73TpAdmnvfFZAHfWxqB1rVIFkAgSwMHzMic= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= @@ -34,10 +34,11 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bradleyfalzon/ghinstallation/v2 v2.8.0 h1:yUmoVv70H3J4UOqxqsee39+KlXxNEDfTbAp8c/qULKk= github.com/bradleyfalzon/ghinstallation/v2 v2.8.0/go.mod h1:fmPmvCiBWhJla3zDv9ZTQSZc8AbwyRnGW1yg5ep1Pcs= +github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -46,6 +47,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0= +github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/docker/docker v27.2.0+incompatible h1:Rk9nIVdfH3+Vz4cyI/uhbINhEZ/oLmc+CBXmH6fbNk4= github.com/docker/docker v27.2.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= @@ -54,8 +57,8 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4 github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= -github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= +github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -68,21 +71,20 @@ github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= -github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= +github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -165,6 +167,8 @@ github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQ github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkoukk/tiktoken-go v0.1.6 h1:JF0TlJzhTbrI30wCvFuiw6FzP2+/bR+FIxUdgEAcUsw= +github.com/pkoukk/tiktoken-go v0.1.6/go.mod h1:9NiV+i9mJKGj1rYOT+njbv+ZwA/zJxYdewGl6qVatpg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= @@ -177,8 +181,8 @@ github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/qiniu/x v1.13.10 h1:J4Z3XugYzAq85SlyAfqlKVrbf05glMbAOh+QncsDQpE= github.com/qiniu/x v1.13.10/go.mod h1:INZ2TSWSJVWO/RuELQROERcslBwVgFG7MkTfEdaQz9E= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -195,6 +199,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tmc/langchaingo v0.1.12 h1:yXwSu54f3b1IKw0jJ5/DWu+qFVH1NBblwC0xddBzGJE= +github.com/tmc/langchaingo v0.1.12/go.mod h1:cd62xD6h+ouk8k/QQFhOsjRYBSA1JJ5UVKXSIgm7Ni4= github.com/xanzy/go-gitlab v0.109.0 h1:RcRme5w8VpLXTSTTMZdVoQWY37qTJWg+gwdQl4aAttE= github.com/xanzy/go-gitlab v0.109.0/go.mod h1:wKNKh3GkYDMOsGmnfuX+ITCmDuSDWFO0G+C4AygL9RY= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -228,8 +234,8 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= -golang.org/x/oauth2 v0.19.0 h1:9+E/EZBCbTLNrbN35fHv/a/d/mOBatymz1zbtQrXpIg= -golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8= +golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= +golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -247,8 +253,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -259,15 +265,13 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc h1:8DyZCyvI8mE1IdLy/60bS+52xfymkE72wv1asokgtao= +google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda h1:wu/KJm9KJwpfHWhkkZGohVC6KRrc1oJNr4jwtQMOQXw= google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd h1:BBOTEWLuuEGQy9n1y9MhVJ9Qt0BDu21X8qZs71/uPZo= google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd/go.mod h1:fO8wJzT2zbQbAjbIoos1285VfEIYKDDY+Dt+WpTkh6g= google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd h1:6TEm2ZxXoQmFWFlt1vNxvVOa1Q0dXFQD1m/rYjXmS0E= google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/linters/agent.go b/internal/linters/agent.go index 52476d32..08eb9c5e 100644 --- a/internal/linters/agent.go +++ b/internal/linters/agent.go @@ -27,8 +27,11 @@ import ( "github.com/qiniu/reviewbot/config" "github.com/qiniu/reviewbot/internal/cache" "github.com/qiniu/reviewbot/internal/lintersutil" + "github.com/qiniu/reviewbot/internal/llm" "github.com/qiniu/reviewbot/internal/runner" "github.com/qiniu/reviewbot/internal/storage" + "github.com/qiniu/x/log" + "github.com/tmc/langchaingo/llms" ) // issueCache is the issue references cache. @@ -59,6 +62,8 @@ type Agent struct { GenLogViewURL func() string // IssueReferences is the compiled issue references config for the linter. IssueReferences []config.CompiledIssueReference + // ModelClient is the LLM model client. + ModelClient llms.Model } // getMsgFormat returns the message format based on report type. @@ -140,6 +145,11 @@ func (a *Agent) ApplyTypedMessageByIssueReferences(ctx context.Context, lintResu } } if !processed { + resp, err := llm.QueryForReference(ctx, a.ModelClient, output.Message) + if err != nil { + log.Errorf("failed to query LLM server: %v", err) + } + output.Message += fmt.Sprintf(ReferenceFooter, resp) newOutputs = append(newOutputs, output) } } @@ -155,5 +165,6 @@ func (a *Agent) ApplyTypedMessageByIssueReferences(ctx context.Context, lintResu const ReferenceFooter = `
Details + %s
` diff --git a/internal/linters/agent_test.go b/internal/linters/agent_test.go index f71fb9b1..dc693d6d 100644 --- a/internal/linters/agent_test.go +++ b/internal/linters/agent_test.go @@ -2,6 +2,7 @@ package linters import ( "context" + "fmt" "reflect" "regexp" "testing" @@ -126,7 +127,7 @@ func TestApplyTypedMessageByIssueReferences(t *testing.T) { expectedOutput: map[string][]LinterOutput{ "file4.go": { { - Message: "regular lint message without issue reference", + Message: "regular lint message without issue reference" + fmt.Sprintf(ReferenceFooter, ""), Line: 40, }, }, @@ -180,7 +181,7 @@ func TestApplyTypedMessageByIssueReferences(t *testing.T) { }, "file6.go": { { - Message: "no issue reference", + Message: "no issue reference" + fmt.Sprintf(ReferenceFooter, ""), Line: 60, }, }, diff --git a/internal/llm/llm.go b/internal/llm/llm.go new file mode 100644 index 00000000..ebab8632 --- /dev/null +++ b/internal/llm/llm.go @@ -0,0 +1,100 @@ +package llm + +import ( + "context" + "errors" + "fmt" + + "github.com/tmc/langchaingo/llms" +) + +type Config struct { + Provider string + APIKey string + Model string + ServerURL string +} + +var ErrUnsupportedProvider = errors.New("unsupported llm provider") +var ErrModelIsNil = errors.New("model is nil") + +func New(ctx context.Context, config Config) (llms.Model, error) { + switch config.Provider { + case "openai": + return initOpenAIClient(config) + case "ollama": + return initOllamaClient(config) + default: + return nil, ErrUnsupportedProvider + } +} + +func Query(ctx context.Context, model llms.Model, query string, extraContext string) (string, error) { + ragQuery := fmt.Sprintf(ragTemplateStr, query, extraContext) + respText, err := llms.GenerateFromSinglePrompt(ctx, model, ragQuery) + if err != nil { + return "", err + } + return respText, nil +} + +const ragTemplateStr = ` +I will ask you a question and will provide some additional context information. +Assume this context information is factual and correct, as part of internal +documentation. +If the question relates to the context, answer it using the context. +If the question does not relate to the context, answer it as normal. + +For example, let's say the context has nothing in it about tropical flowers; +then if I ask you about tropical flowers, just answer what you know about them +without referring to the context. + +For example, if the context does mention minerology and I ask you about that, +provide information from the context along with general knowledge. + +Question: +%s + +Context: +%s +` + +func QueryForReference(ctx context.Context, model llms.Model, linterOutput string) (string, error) { + if model == nil { + return "", ErrModelIsNil + } + ragQuery := fmt.Sprintf(referenceTemplateStr, linterOutput) + respText, err := llms.GenerateFromSinglePrompt(ctx, model, ragQuery) + if err != nil { + return "", err + } + return respText, nil +} + +const referenceTemplateStr = ` +You are a lint expert, you can explain in detail the meaning of the lint result according to the content of the given context. +You will follow the format of the example in to answer in Chinese, firstly, you will explain the lint, secondly, you will give the incorrect usage (it can be a code example or text description), and finally, you will give the correct usage (it can be a code example or a text description), and finally add a blank line before the result. + + + +### lint 解释 +- 该 lint 出现表示一个变量被赋值后又被重新赋值,但在此过程中没有使用其原始值。这种情况通常表明代码中可能存在冗余,或者可能是逻辑错误,因为原始值在此期间没有被实际使用。 +### 错误用法 + // + func main() { + x := 10 + x = 20 // 重新赋值,但未使用原始值 + + fmt.Println("x =", x) // 仅打印新值20 + } + + +### 正确用法 +- 方案一:使用赋值变量 +- 方案二 :移除冗余赋值 + + +Context: +%s + +` diff --git a/internal/llm/ollama.go b/internal/llm/ollama.go new file mode 100644 index 00000000..9e7bc433 --- /dev/null +++ b/internal/llm/ollama.go @@ -0,0 +1,29 @@ +package llm + +import ( + "errors" + + "github.com/tmc/langchaingo/llms" + "github.com/tmc/langchaingo/llms/ollama" +) + +var ErrServerURLRequired = errors.New("server URL is required") +var ErrModelRequired = errors.New("model is required") + +func initOllamaClient(config Config) (llms.Model, error) { + if config.ServerURL == "" { + return nil, ErrServerURLRequired + } + if config.Model == "" { + return nil, ErrModelRequired + } + opts := []ollama.Option{ + ollama.WithServerURL(config.ServerURL), + ollama.WithModel(config.Model), + } + m, err := ollama.New(opts...) + if err != nil { + return nil, err + } + return m, nil +} diff --git a/internal/llm/openapi.go b/internal/llm/openapi.go new file mode 100644 index 00000000..41c81ac2 --- /dev/null +++ b/internal/llm/openapi.go @@ -0,0 +1,26 @@ +package llm + +import ( + "errors" + + "github.com/tmc/langchaingo/llms" + "github.com/tmc/langchaingo/llms/openai" +) + +var ErrAPIKeyRequired = errors.New("API key is required") + +func initOpenAIClient(config Config) (llms.Model, error) { + if config.APIKey == "" { + return nil, ErrAPIKeyRequired + } + opts := []openai.Option{ + openai.WithModel(config.Model), + openai.WithToken(config.APIKey), + openai.WithBaseURL(config.ServerURL), + } + m, err := openai.New(opts...) + if err != nil { + return nil, err + } + return m, nil +} diff --git a/main.go b/main.go index e45c90fb..0d5f9c22 100644 --- a/main.go +++ b/main.go @@ -31,6 +31,7 @@ import ( "github.com/google/go-github/v57/github" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/qiniu/reviewbot/config" + "github.com/qiniu/reviewbot/internal/llm" "github.com/qiniu/reviewbot/internal/storage" "github.com/qiniu/reviewbot/internal/version" "github.com/qiniu/x/log" @@ -78,11 +79,19 @@ type options struct { serverAddr string // kube config file kubeConfig string + + // llm related + llmProvider string + llmModel string + llmServerURL string + llmAPIKey string } var ( - errAppNotSet = errors.New("app-private-key is required when using github app") - errWebHookNotSet = errors.New("webhook-secret is required") + errAppNotSet = errors.New("app-private-key is required when using github app") + errWebHookNotSet = errors.New("webhook-secret is required") + errLLMKeyNotSet = errors.New("llm api key is not set") + errLLMServerNotSet = errors.New("llm model or server url is not set") ) func (o options) Validate() error { @@ -93,6 +102,19 @@ func (o options) Validate() error { if o.webhookSecret == "" { return errWebHookNotSet } + + if o.llmProvider != "" { + switch o.llmProvider { + case "openai": + if o.llmAPIKey == "" { + return errLLMKeyNotSet + } + case "ollama": + if o.llmModel == "" || o.llmServerURL == "" { + return errLLMServerNotSet + } + } + } return nil } @@ -120,6 +142,12 @@ func gatherOptions() options { fs.StringVar(&o.gitLabPersonalAccessToken, "gitlab.personal-access-token", "", "personal gitlab access token") fs.StringVar(&o.gitLabHost, "gitlab.host", "", "gitlab server") + // llm related + fs.StringVar(&o.llmProvider, "llm.provider", "", "llm provider") + fs.StringVar(&o.llmModel, "llm.model", "", "llm model") + fs.StringVar(&o.llmServerURL, "llm.server-url", "", "llm server url") + fs.StringVar(&o.llmAPIKey, "llm.api-key", "", "llm api key") + err := fs.Parse(os.Args[1:]) if err != nil { log.Fatalf("failed to parse flags: %v", err) @@ -232,6 +260,13 @@ func main() { } } + modelConfig := llm.Config{ + Provider: o.llmProvider, + APIKey: o.llmAPIKey, + Model: o.llmModel, + ServerURL: o.llmServerURL, + } + s := &Server{ webhookSecret: []byte(o.webhookSecret), gitClientFactory: v2, @@ -243,6 +278,7 @@ func main() { gitLabHost: o.gitLabHost, gitLabPersonalAccessToken: o.gitLabPersonalAccessToken, gitHubPersonalAccessToken: o.gitHubPersonalAccessToken, + modelConfig: modelConfig, } // github app @@ -254,6 +290,7 @@ func main() { } } + s.initLLMModel() go s.initDockerRunner() go s.initKubernetesRunner() s.initCustomLinters() diff --git a/server.go b/server.go index ab3427d8..17d62bb6 100644 --- a/server.go +++ b/server.go @@ -39,9 +39,11 @@ import ( "github.com/qiniu/reviewbot/config" "github.com/qiniu/reviewbot/internal/linters" "github.com/qiniu/reviewbot/internal/lintersutil" + "github.com/qiniu/reviewbot/internal/llm" "github.com/qiniu/reviewbot/internal/runner" "github.com/qiniu/reviewbot/internal/storage" "github.com/qiniu/x/log" + "github.com/tmc/langchaingo/llms" "github.com/xanzy/go-gitlab" gitv2 "sigs.k8s.io/prow/pkg/git/v2" ) @@ -78,6 +80,9 @@ type Server struct { // gitHubAppPrivateKey string gitHubAppAuth *GitHubAppAuth gitHubPersonalAccessToken string + + modelConfig llm.Config + modelClient llms.Model } func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { @@ -113,6 +118,18 @@ func (s *Server) serveGitHub(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "Event received. Have a nice day.") switch event := event.(type) { + case *github.IssueCommentEvent: + go func() { + if err := s.processIssueCommentEvent(ctx, event); err != nil { + log.Errorf("process issue comment event: %v", err) + } + }() + case *github.PullRequestReviewCommentEvent: + go func() { + if err := s.processPullRequestReviewCommentEvent(ctx, event); err != nil { + log.Errorf("process pull request review comment event: %v", err) + } + }() case *github.PullRequestEvent: go func() { if err := s.processPullRequestEvent(ctx, event); err != nil { @@ -310,6 +327,9 @@ func (s *Server) handleCodeRequestEvent(ctx context.Context, info *codeRequestIn // set issue references agent.IssueReferences = s.config.GetCompiledIssueReferences(name) + // set model client + agent.ModelClient = s.modelClient + // run linter finally if err := fn(ctx, agent); err != nil { if errors.Is(err, context.Canceled) { @@ -349,6 +369,14 @@ func (s *Server) withCancel(ctx context.Context, info *codeRequestInfo, fn func( return fn(ctx) } +func (s *Server) initLLMModel() { + modelClient, err := llm.New(context.Background(), s.modelConfig) + if err != nil { + log.Fatalf("failed to init rag server: %v", err) + } + s.modelClient = modelClient +} + func (s *Server) initCustomLinters() { for linterName, customLinter := range s.config.CustomLinters { linters.RegisterPullRequestHandler(linterName, linters.GeneralLinterHandler) @@ -482,6 +510,79 @@ func (s *Server) pullImageWithRetry(ctx context.Context, image string, dockerRun } } +func (s *Server) processIssueCommentEvent(ctx context.Context, event *github.IssueCommentEvent) error { + log := lintersutil.FromContext(ctx) + if event.GetAction() != "created" { + log.Debugf("skipping action %s\n", event.GetAction()) + return nil + } + if !strings.Contains(*event.Comment.Body, "reviewbot") { + return nil + } + + query := event.GetComment().GetBody() + resp, err := llm.Query(ctx, s.modelClient, query, "") + if err != nil { + return err + } + log.Infof("query success,got resp: %s", resp) + + replyComment := &github.IssueComment{ + Body: github.String(resp), + } + installationID := event.GetInstallation().GetID() + _, _, err = s.GithubClient(installationID).Issues.CreateComment(ctx, event.GetRepo().GetOwner().GetLogin(), event.GetRepo().GetName(), event.GetIssue().GetNumber(), replyComment) + if err != nil { + return err + } + log.Infof("create comment success: %v", replyComment) + return nil +} + +func (s *Server) processPullRequestReviewCommentEvent(ctx context.Context, event *github.PullRequestReviewCommentEvent) error { + log := lintersutil.FromContext(ctx) + if event.GetAction() != "created" { + log.Debugf("skipping action %s\n", event.GetAction()) + return nil + } + + query := event.GetComment().GetBody() + path := event.GetComment().GetPath() + position := event.GetComment().GetPosition() + diffHunk := event.GetComment().GetDiffHunk() + inReplyTo := event.GetComment().GetInReplyTo() + + if !strings.Contains(query, "reviewbot") { + log.Debugf("skipping reviewbot comment\n") + return nil + } + + installationID := event.GetInstallation().GetID() + githubClient := s.GithubClient(installationID) + historyComments, err := prepareCommentContext(ctx, event, githubClient, path, position) + if err != nil { + return err + } + queryContext := "diffHunk: " + diffHunk + "\n" + "history comments: " + historyComments + + // send query to llm model + resp, err := llm.Query(ctx, s.modelClient, query, queryContext) + if err != nil { + return err + } + log.Infof("query success,got resp: %s", resp) + + // reply comment + comment, _, err := githubClient.PullRequests.CreateCommentInReplyTo(ctx, event.GetRepo().GetOwner().GetLogin(), event.GetRepo().GetName(), event.GetPullRequest().GetNumber(), resp, inReplyTo) + if err != nil { + log.Errorf("failed to create comment: %v", err) + return err + } + + log.Infof("create comment success: %v", comment) + return nil +} + func (s *Server) processPullRequestEvent(ctx context.Context, event *github.PullRequestEvent) error { log := lintersutil.FromContext(ctx) if event.GetAction() != "opened" && event.GetAction() != "reopened" && event.GetAction() != "synchronize" { @@ -646,3 +747,24 @@ func checkKubectlInstalled() error { cmd := exec.Command("kubectl", "version", "--client") return cmd.Run() } + +func prepareCommentContext(ctx context.Context, event *github.PullRequestReviewCommentEvent, githubClient *github.Client, path string, position int) (string, error) { + // get all comments + prComments, _, err := githubClient.PullRequests.ListComments(ctx, event.GetRepo().GetOwner().GetLogin(), event.GetRepo().GetName(), event.GetPullRequest().GetNumber(), nil) + if err != nil { + return "", err + } + + // filter comments by path and position + var comments []string + if path != "" && position != 0 { + for _, comment := range prComments { + if comment.GetPath() == path && comment.GetPosition() == position { + comments = append(comments, comment.GetBody()) + } + } + } + log.Infof("filter comments success: %v", comments) + + return strings.Join(comments, "\n"), nil +}