first commit
This commit is contained in:
0
plugins/jsonparser/changelog.md
Normal file
0
plugins/jsonparser/changelog.md
Normal file
70
plugins/jsonparser/go.mod
Normal file
70
plugins/jsonparser/go.mod
Normal file
@@ -0,0 +1,70 @@
|
||||
module github.com/maximhq/bifrost/plugins/jsonparser
|
||||
|
||||
go 1.26.2
|
||||
|
||||
require github.com/maximhq/bifrost/core v1.5.4
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.123.0 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.9.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 // indirect
|
||||
github.com/andybalholm/brotli v1.2.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.14 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.9 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.15 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 // indirect
|
||||
github.com/aws/smithy-go v1.24.2 // indirect
|
||||
github.com/bahlo/generic-list-go v0.2.0 // indirect
|
||||
github.com/buger/jsonparser v1.1.2 // indirect
|
||||
github.com/bytedance/gopkg v0.1.3 // indirect
|
||||
github.com/bytedance/sonic v1.15.0 // indirect
|
||||
github.com/bytedance/sonic/loader v0.5.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/invopop/jsonschema v0.13.0 // indirect
|
||||
github.com/klauspost/compress v1.18.2 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/mailru/easyjson v0.9.1 // indirect
|
||||
github.com/mark3labs/mcp-go v0.43.2 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||
github.com/rs/zerolog v1.34.0 // indirect
|
||||
github.com/spf13/cast v1.10.0 // indirect
|
||||
github.com/tidwall/gjson v1.18.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
github.com/tidwall/sjson v1.2.5 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.68.0 // indirect
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
|
||||
go.starlark.net v0.0.0-20260102030733-3fee463870c9 // indirect
|
||||
golang.org/x/arch v0.23.0 // indirect
|
||||
golang.org/x/crypto v0.49.0 // indirect
|
||||
golang.org/x/net v0.52.0 // indirect
|
||||
golang.org/x/oauth2 v0.36.0 // indirect
|
||||
golang.org/x/sys v0.42.0 // indirect
|
||||
golang.org/x/text v0.35.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
185
plugins/jsonparser/go.sum
Normal file
185
plugins/jsonparser/go.sum
Normal file
@@ -0,0 +1,185 @@
|
||||
cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE=
|
||||
cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU=
|
||||
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
|
||||
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 h1:JXg2dwJUmPB9JmtVmdEB16APJ7jurfbY5jnfXpJoRMc=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI=
|
||||
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
|
||||
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk=
|
||||
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
|
||||
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.5 h1:dj5kopbwUsVUVFgO4Fi5BIT3t4WyqIDjGKCangnV/yY=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.5/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 h1:eBMB84YGghSocM7PsjmmPffTa+1FBUeNvGvFou6V/4o=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8/go.mod h1:lyw7GFp3qENLh7kwzf7iMzAxDn+NzjXEAGjKS2UOKqI=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.11 h1:ftxI5sgz8jZkckuUHXfC/wMUc8u3fG1vQS0plr2F2Zs=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.11/go.mod h1:twF11+6ps9aNRKEDimksp923o44w/Thk9+8YIlzWMmo=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.14 h1:n+UcGWAIZHkXzYt87uMFBv/l8THYELoX6gVcUvgl6fI=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.14/go.mod h1:cJKuyWB59Mqi0jM3nFYQRmnHVQIcgoxjEMAbLkpr62w=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21 h1:NUS3K4BTDArQqNu2ih7yeDLaS3bmHD0YndtA6UP884g=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21/go.mod h1:YWNWJQNjKigKY1RHVJCuupeWDrrHjRqHm0N9rdrWzYI=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 h1:Rgg6wvjjtX8bNHcvi9OnXWwcE0a2vGpbwmtICOsvcf4=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21/go.mod h1:A/kJFst/nm//cyqonihbdpQZwiUhhzpqTsdbhDdRF9c=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 h1:PEgGVtPoB6NTpPrBgqSE5hE/o47Ij9qk/SEZFbUOe9A=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21/go.mod h1:p+hz+PRAYlY3zcpJhPwXlLC4C+kqn70WIHwnzAfs6ps=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.5 h1:clHU5fm//kWS1C2HgtgWxfQbFbx4b6rx+5jzhgX9HrI=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.5/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 h1:rWyie/PxDRIdhNf4DzRk0lvjVOqFJuNnO8WwaIRVxzQ=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22/go.mod h1:zd/JsJ4P7oGfUhXn1VyLqaRZwPmZwg44Jf2dS84Dm3Y=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13 h1:JRaIgADQS/U6uXDqlPiefP32yXTda7Kqfx+LgspooZM=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13/go.mod h1:CEuVn5WqOMilYl+tbccq8+N2ieCy0gVn3OtRb0vBNNM=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 h1:c31//R3xgIJMSC8S6hEVq+38DcvUlgFY0FM6mSI5oto=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21/go.mod h1:r6+pf23ouCB718FUxaqzZdbpYFyDtehyZcmP5KL9FkA=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21 h1:ZlvrNcHSFFWURB8avufQq9gFsheUgjVD9536obIknfM=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21/go.mod h1:cv3TNhVrssKR0O/xxLJVRfd2oazSnZnkUeTf6ctUwfQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.3 h1:HwxWTbTrIHm5qY+CAEur0s/figc3qwvLWsNkF4RPToo=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.3/go.mod h1:uoA43SdFwacedBfSgfFSjjCvYe8aYBS7EnU5GZ/YKMM=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.9 h1:QKZH0S178gCmFEgst8hN0mCX1KxLgHBKKY/CLqwP8lg=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.9/go.mod h1:7yuQJoT+OoH8aqIxw9vwF+8KpvLZ8AWmvmUWHsGQZvI=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.15 h1:lFd1+ZSEYJZYvv9d6kXzhkZu07si3f+GQ1AaYwa2LUM=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.15/go.mod h1:WSvS1NLr7JaPunCXqpJnWk1Bjo7IxzZXrZi1QQCkuqM=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19 h1:dzztQ1YmfPrxdrOiuZRMF6fuOwWlWpD2StNLTceKpys=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19/go.mod h1:YO8TrYtFdl5w/4vmjL8zaBSsiNp3w0L1FfKVKenZT7w=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 h1:p8ogvvLugcR/zLBXTXrTkj0RYBUdErbMnAFFp12Lm/U=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.10/go.mod h1:60dv0eZJfeVXfbT1tFJinbHrDfSJ2GZl4Q//OSSNAVw=
|
||||
github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng=
|
||||
github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
|
||||
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
|
||||
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
|
||||
github.com/buger/jsonparser v1.1.2 h1:frqHqw7otoVbk5M8LlE/L7HTnIq2v9RX6EJ48i9AxJk=
|
||||
github.com/buger/jsonparser v1.1.2/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
|
||||
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
|
||||
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
|
||||
github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE=
|
||||
github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k=
|
||||
github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE=
|
||||
github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
|
||||
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
|
||||
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fasthttp/websocket v1.5.12 h1:e4RGPpWW2HTbL3zV0Y/t7g0ub294LkiuXXUuTOUInlE=
|
||||
github.com/fasthttp/websocket v1.5.12/go.mod h1:I+liyL7/4moHojiOgUOIKEWm9EIxHqxZChS+aMFltyg=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hajimehoshi/go-mp3 v0.3.4 h1:NUP7pBYH8OguP4diaTZ9wJbUbk3tC0KlfzsEpWmYj68=
|
||||
github.com/hajimehoshi/go-mp3 v0.3.4/go.mod h1:fRtZraRFcWb0pu7ok0LqyFhCUrPeMsGRSVop0eemFmo=
|
||||
github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
|
||||
github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
|
||||
github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU=
|
||||
github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k=
|
||||
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
|
||||
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=
|
||||
github.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
||||
github.com/mark3labs/mcp-go v0.43.2 h1:21PUSlWWiSbUPQwXIJ5WKlETixpFpq+WBpbMGDSVy/I=
|
||||
github.com/mark3labs/mcp-go v0.43.2/go.mod h1:YnJfOL382MIWDx1kMY+2zsRHU/q78dBg9aFb8W6Thdw=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/maximhq/bifrost/core v1.5.4 h1:hf0BhoHVVpY1EQ4FkyRzW4IBYjrolxdZV0ucgWfHhcE=
|
||||
github.com/maximhq/bifrost/core v1.5.4/go.mod h1:z1/vOalbDAD7v7sYbXQsqR+2qIFP0jKOSIStw6Q4P4U=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
|
||||
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
|
||||
github.com/savsgio/gotils v0.0.0-20250408102913-196191ec6287 h1:qIQ0tWF9vxGtkJa24bR+2i53WBCz1nW/Pc47oVYauC4=
|
||||
github.com/savsgio/gotils v0.0.0-20250408102913-196191ec6287/go.mod h1:sM7Mt7uEoCeFSCBM+qBrqvEo+/9vdmj19wzp3yzUhmg=
|
||||
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
||||
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
||||
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.68.0 h1:v12Nx16iepr8r9ySOwqI+5RBJ/DqTxhOy1HrHoDFnok=
|
||||
github.com/valyala/fasthttp v1.68.0/go.mod h1:5EXiRfYQAoiO/khu4oU9VISC/eVY6JqmSpPJoHCKsz4=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
|
||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
|
||||
go.starlark.net v0.0.0-20260102030733-3fee463870c9 h1:nV1OyvU+0CYrp5eKfQ3rD03TpFYYhH08z31NK1HmtTk=
|
||||
go.starlark.net v0.0.0-20260102030733-3fee463870c9/go.mod h1:YKMCv9b1WrfWmeqdV5MAuEHWsu5iC+fe6kYl2sQjdI8=
|
||||
golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg=
|
||||
golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
|
||||
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
|
||||
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
|
||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
||||
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
|
||||
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
|
||||
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
262
plugins/jsonparser/main.go
Normal file
262
plugins/jsonparser/main.go
Normal file
@@ -0,0 +1,262 @@
|
||||
package jsonparser
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
bifrost "github.com/maximhq/bifrost/core"
|
||||
"github.com/maximhq/bifrost/core/schemas"
|
||||
)
|
||||
|
||||
const (
|
||||
PluginName = "streaming-json-parser"
|
||||
)
|
||||
|
||||
type Usage string
|
||||
|
||||
const (
|
||||
AllRequests Usage = "all_requests"
|
||||
PerRequest Usage = "per_request"
|
||||
)
|
||||
|
||||
// AccumulatedContent holds both the content and timestamp for a request
|
||||
type AccumulatedContent struct {
|
||||
Content *strings.Builder
|
||||
Timestamp time.Time
|
||||
}
|
||||
|
||||
// JsonParserPlugin provides JSON parsing capabilities for streaming responses
|
||||
// It handles partial JSON chunks by accumulating them and making the accumulated content valid JSON
|
||||
type JsonParserPlugin struct {
|
||||
usage Usage
|
||||
// State management for accumulating chunks
|
||||
accumulatedContent map[string]*AccumulatedContent // requestID -> accumulated content with timestamp
|
||||
mutex sync.RWMutex
|
||||
// Cleanup configuration
|
||||
cleanupInterval time.Duration
|
||||
maxAge time.Duration
|
||||
stopCleanup chan struct{}
|
||||
stopOnce sync.Once
|
||||
}
|
||||
|
||||
// PluginConfig holds configuration options for the JSON parser plugin
|
||||
type PluginConfig struct {
|
||||
Usage Usage
|
||||
CleanupInterval time.Duration
|
||||
MaxAge time.Duration
|
||||
}
|
||||
|
||||
const (
|
||||
EnableStreamingJSONParser schemas.BifrostContextKey = "enable-streaming-json-parser"
|
||||
)
|
||||
|
||||
// Init creates a new JSON parser plugin instance with custom configuration
|
||||
func Init(config PluginConfig) (*JsonParserPlugin, error) {
|
||||
// Set defaults if not provided
|
||||
if config.CleanupInterval <= 0 {
|
||||
config.CleanupInterval = 5 * time.Minute
|
||||
}
|
||||
if config.MaxAge <= 0 {
|
||||
config.MaxAge = 30 * time.Minute
|
||||
}
|
||||
if config.Usage == "" {
|
||||
config.Usage = PerRequest
|
||||
}
|
||||
|
||||
plugin := &JsonParserPlugin{
|
||||
usage: config.Usage,
|
||||
accumulatedContent: make(map[string]*AccumulatedContent),
|
||||
cleanupInterval: config.CleanupInterval,
|
||||
maxAge: config.MaxAge,
|
||||
stopCleanup: make(chan struct{}),
|
||||
}
|
||||
|
||||
// Start the cleanup goroutine
|
||||
go plugin.startCleanupGoroutine()
|
||||
|
||||
return plugin, nil
|
||||
}
|
||||
|
||||
// GetName returns the plugin name
|
||||
func (p *JsonParserPlugin) GetName() string {
|
||||
return PluginName
|
||||
}
|
||||
|
||||
// HTTPTransportPreHook is not used for this plugin
|
||||
func (p *JsonParserPlugin) HTTPTransportPreHook(ctx *schemas.BifrostContext, req *schemas.HTTPRequest) (*schemas.HTTPResponse, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// HTTPTransportPostHook is not used for this plugin
|
||||
func (p *JsonParserPlugin) HTTPTransportPostHook(ctx *schemas.BifrostContext, req *schemas.HTTPRequest, resp *schemas.HTTPResponse) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// HTTPTransportStreamChunkHook passes through streaming chunks unchanged
|
||||
func (p *JsonParserPlugin) HTTPTransportStreamChunkHook(ctx *schemas.BifrostContext, req *schemas.HTTPRequest, chunk *schemas.BifrostStreamChunk) (*schemas.BifrostStreamChunk, error) {
|
||||
return chunk, nil
|
||||
}
|
||||
|
||||
// PreLLMHook is not used for this plugin as we only process responses
|
||||
// Parameters:
|
||||
// - ctx: The Bifrost context
|
||||
// - req: The Bifrost request
|
||||
//
|
||||
// Returns:
|
||||
// - *schemas.BifrostRequest: The processed request
|
||||
// - *schemas.LLMPluginShortCircuit: The plugin short circuit if the request is not allowed
|
||||
// - error: Any error that occurred during processing
|
||||
func (p *JsonParserPlugin) PreLLMHook(ctx *schemas.BifrostContext, req *schemas.BifrostRequest) (*schemas.BifrostRequest, *schemas.LLMPluginShortCircuit, error) {
|
||||
return req, nil, nil
|
||||
}
|
||||
|
||||
// PostLLMHook processes streaming responses by accumulating chunks and making accumulated content valid JSON
|
||||
// Parameters:
|
||||
// - ctx: The Bifrost context
|
||||
// - result: The Bifrost response to be processed
|
||||
// - err: The Bifrost error to be processed
|
||||
//
|
||||
// Returns:
|
||||
// - *schemas.BifrostResponse: The processed response
|
||||
// - *schemas.BifrostError: The processed error
|
||||
// - error: Any error that occurred during processing
|
||||
func (p *JsonParserPlugin) PostLLMHook(ctx *schemas.BifrostContext, result *schemas.BifrostResponse, err *schemas.BifrostError) (*schemas.BifrostResponse, *schemas.BifrostError, error) {
|
||||
// If there's an error, don't process
|
||||
if err != nil {
|
||||
return result, err, nil
|
||||
}
|
||||
|
||||
extraFields := result.GetExtraFields()
|
||||
|
||||
// Check if plugin should run based on usage type
|
||||
if !p.shouldRun(ctx, extraFields.RequestType) {
|
||||
return result, err, nil
|
||||
}
|
||||
|
||||
// If no chat response, return as is
|
||||
if result == nil || result.ChatResponse == nil {
|
||||
return result, err, nil
|
||||
}
|
||||
|
||||
// Get request ID for state management, if it's not set, return as is
|
||||
requestID := p.getRequestID(ctx, result)
|
||||
if requestID == "" {
|
||||
return result, err, nil
|
||||
}
|
||||
|
||||
// Create a deep copy of the result to avoid modifying the original pointer
|
||||
// This ensures other plugins using the same pointer don't get corrupted data
|
||||
resultCopy := p.deepCopyBifrostResponse(result)
|
||||
if resultCopy == nil || resultCopy.ChatResponse == nil {
|
||||
return result, err, nil
|
||||
}
|
||||
|
||||
// Process only streaming choices to accumulate and fix partial JSON
|
||||
if len(resultCopy.ChatResponse.Choices) > 0 {
|
||||
for i := range resultCopy.ChatResponse.Choices {
|
||||
choice := &resultCopy.ChatResponse.Choices[i]
|
||||
|
||||
// Handle only streaming response
|
||||
if choice.ChatStreamResponseChoice != nil {
|
||||
if choice.ChatStreamResponseChoice.Delta.Content != nil {
|
||||
content := *choice.ChatStreamResponseChoice.Delta.Content
|
||||
if content != "" {
|
||||
// Accumulate the content
|
||||
accumulated := p.accumulateContent(requestID, content)
|
||||
|
||||
// Process the accumulated content to make it valid JSON
|
||||
fixedContent := p.parsePartialJSON(accumulated)
|
||||
|
||||
if !p.isValidJSON(fixedContent) {
|
||||
err = &schemas.BifrostError{
|
||||
Error: &schemas.ErrorField{
|
||||
Message: "Invalid JSON in streaming response",
|
||||
},
|
||||
StreamControl: &schemas.StreamControl{
|
||||
SkipStream: bifrost.Ptr(true),
|
||||
},
|
||||
}
|
||||
|
||||
return nil, err, nil
|
||||
}
|
||||
|
||||
// Replace the delta content with the complete valid JSON
|
||||
choice.ChatStreamResponseChoice.Delta.Content = &fixedContent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If this is the final chunk, cleanup the accumulated content for this request
|
||||
if streamEndIndicatorValue := ctx.Value(schemas.BifrostContextKeyStreamEndIndicator); streamEndIndicatorValue != nil {
|
||||
isFinalChunk, ok := streamEndIndicatorValue.(bool)
|
||||
if ok && isFinalChunk {
|
||||
p.ClearRequestState(requestID)
|
||||
}
|
||||
}
|
||||
|
||||
// Return the modified copy instead of the original
|
||||
return resultCopy, err, nil
|
||||
}
|
||||
|
||||
// Cleanup performs plugin cleanup and clears accumulated content
|
||||
func (p *JsonParserPlugin) Cleanup() error {
|
||||
// Stop the cleanup goroutine
|
||||
p.StopCleanup()
|
||||
|
||||
p.mutex.Lock()
|
||||
defer p.mutex.Unlock()
|
||||
|
||||
// Clear accumulated content
|
||||
p.accumulatedContent = make(map[string]*AccumulatedContent)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ClearRequestState clears the accumulated content for a specific request
|
||||
func (p *JsonParserPlugin) ClearRequestState(requestID string) {
|
||||
p.mutex.Lock()
|
||||
defer p.mutex.Unlock()
|
||||
|
||||
delete(p.accumulatedContent, requestID)
|
||||
}
|
||||
|
||||
// CLEANUP METHODS
|
||||
|
||||
// startCleanupGoroutine starts a goroutine that periodically cleans up old accumulated content
|
||||
func (p *JsonParserPlugin) startCleanupGoroutine() {
|
||||
ticker := time.NewTicker(p.cleanupInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
p.cleanupOldEntries()
|
||||
case <-p.stopCleanup:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// cleanupOldEntries removes accumulated content entries that are older than maxAge
|
||||
func (p *JsonParserPlugin) cleanupOldEntries() {
|
||||
p.mutex.Lock()
|
||||
defer p.mutex.Unlock()
|
||||
|
||||
now := time.Now()
|
||||
cutoff := now.Add(-p.maxAge)
|
||||
|
||||
for requestID, content := range p.accumulatedContent {
|
||||
if content.Timestamp.Before(cutoff) {
|
||||
delete(p.accumulatedContent, requestID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// StopCleanup stops the cleanup goroutine
|
||||
func (p *JsonParserPlugin) StopCleanup() {
|
||||
p.stopOnce.Do(func() {
|
||||
close(p.stopCleanup)
|
||||
})
|
||||
}
|
||||
323
plugins/jsonparser/plugin_test.go
Normal file
323
plugins/jsonparser/plugin_test.go
Normal file
@@ -0,0 +1,323 @@
|
||||
package jsonparser
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
bifrost "github.com/maximhq/bifrost/core"
|
||||
"github.com/maximhq/bifrost/core/schemas"
|
||||
)
|
||||
|
||||
// BaseAccount implements the schemas.Account interface for testing purposes.
|
||||
// It provides mock implementations of the required methods to test the JSON parser plugin
|
||||
// with a basic OpenAI configuration.
|
||||
type BaseAccount struct{}
|
||||
|
||||
// GetConfiguredProviders returns a list of supported providers for testing.
|
||||
// Currently only supports OpenAI for simplicity in testing.
|
||||
func (baseAccount *BaseAccount) GetConfiguredProviders() ([]schemas.ModelProvider, error) {
|
||||
return []schemas.ModelProvider{schemas.OpenAI}, nil
|
||||
}
|
||||
|
||||
// GetKeysForProvider returns a mock API key configuration for testing.
|
||||
// Uses the OPENAI_API_KEY environment variable for authentication.
|
||||
func (baseAccount *BaseAccount) GetKeysForProvider(ctx context.Context, providerKey schemas.ModelProvider) ([]schemas.Key, error) {
|
||||
return []schemas.Key{
|
||||
{
|
||||
Value: *schemas.NewEnvVar("env.OPENAI_API_KEY"),
|
||||
Models: []string{"gpt-4o-mini", "gpt-4-turbo"},
|
||||
Weight: 1.0,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetConfigForProvider returns default provider configuration for testing.
|
||||
// Uses standard network and concurrency settings.
|
||||
func (baseAccount *BaseAccount) GetConfigForProvider(providerKey schemas.ModelProvider) (*schemas.ProviderConfig, error) {
|
||||
return &schemas.ProviderConfig{
|
||||
NetworkConfig: schemas.DefaultNetworkConfig,
|
||||
ConcurrencyAndBufferSize: schemas.DefaultConcurrencyAndBufferSize,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// TestJsonParserPluginEndToEnd tests the integration of the JSON parser plugin with Bifrost.
|
||||
// It performs the following steps:
|
||||
// 1. Initializes the JSON parser plugin with AllRequests usage
|
||||
// 2. Sets up a test Bifrost instance with the plugin
|
||||
// 3. Makes a test chat completion request with streaming enabled
|
||||
// 4. Verifies that the plugin processes the streaming response correctly
|
||||
//
|
||||
// Required environment variables:
|
||||
// - OPENAI_API_KEY: Your OpenAI API key for the test request
|
||||
func TestJsonParserPluginEndToEnd(t *testing.T) {
|
||||
ctx := schemas.NewBifrostContext(context.Background(), schemas.NoDeadline)
|
||||
// Check if OpenAI API key is set
|
||||
if os.Getenv("OPENAI_API_KEY") == "" {
|
||||
t.Skip("OPENAI_API_KEY is not set, skipping end-to-end test")
|
||||
}
|
||||
|
||||
// Initialize the JSON parser plugin for all requests
|
||||
plugin, err := Init(PluginConfig{
|
||||
Usage: AllRequests,
|
||||
CleanupInterval: 5 * time.Minute,
|
||||
MaxAge: 30 * time.Minute,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error initializing JSON parser plugin: %v", err)
|
||||
}
|
||||
|
||||
account := BaseAccount{}
|
||||
|
||||
// Initialize Bifrost with the plugin
|
||||
client, err := bifrost.Init(ctx, schemas.BifrostConfig{
|
||||
Account: &account,
|
||||
LLMPlugins: []schemas.LLMPlugin{plugin},
|
||||
Logger: bifrost.NewDefaultLogger(schemas.LogLevelDebug),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error initializing Bifrost: %v", err)
|
||||
}
|
||||
defer client.Shutdown()
|
||||
|
||||
// Make a test responses request with streaming enabled
|
||||
// Request JSON output to test the parser
|
||||
var responseFormat interface{} = map[string]interface{}{
|
||||
"type": "json_object",
|
||||
}
|
||||
|
||||
request := &schemas.BifrostChatRequest{
|
||||
Provider: schemas.OpenAI,
|
||||
Model: "gpt-4o-mini",
|
||||
Input: []schemas.ChatMessage{
|
||||
{
|
||||
Role: schemas.ChatMessageRoleUser,
|
||||
Content: &schemas.ChatMessageContent{
|
||||
ContentStr: bifrost.Ptr("Return a JSON object with name, age, and city fields. Example: {\"name\": \"John\", \"age\": 30, \"city\": \"New York\"}"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Params: &schemas.ChatParameters{
|
||||
ResponseFormat: &responseFormat,
|
||||
},
|
||||
}
|
||||
// Make the streaming request
|
||||
responseChan, bifrostErr := client.ChatCompletionStreamRequest(ctx, request)
|
||||
|
||||
if bifrostErr != nil {
|
||||
t.Fatalf("Error in Bifrost request: %v", bifrostErr)
|
||||
}
|
||||
|
||||
// Process streaming responses
|
||||
if responseChan != nil {
|
||||
t.Logf("Streaming response channel received")
|
||||
|
||||
// Read from the channel to see the streaming responses
|
||||
responseCount := 0
|
||||
|
||||
for streamResponse := range responseChan {
|
||||
responseCount++
|
||||
|
||||
if streamResponse.BifrostError != nil {
|
||||
t.Logf("Streaming response error: %v", streamResponse.BifrostError)
|
||||
}
|
||||
|
||||
if streamResponse.BifrostChatResponse != nil {
|
||||
if streamResponse.BifrostChatResponse.Choices != nil {
|
||||
for _, outputMsg := range streamResponse.BifrostChatResponse.Choices {
|
||||
if outputMsg.ChatStreamResponseChoice != nil && outputMsg.ChatStreamResponseChoice.Delta.Content != nil {
|
||||
content := *outputMsg.ChatStreamResponseChoice.Delta.Content
|
||||
if content != "" {
|
||||
t.Logf("Chunk %d: %s", responseCount, content)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
t.Logf("Stream completed after %d responses", responseCount)
|
||||
} else {
|
||||
t.Logf("No streaming response channel received")
|
||||
}
|
||||
|
||||
t.Log("End-to-end test completed - check logs for JSON parsing behavior")
|
||||
}
|
||||
|
||||
// TestJsonParserPluginPerRequest tests the per-request configuration of the JSON parser plugin.
|
||||
// It tests how the plugin behaves when enabled via context for specific requests.
|
||||
//
|
||||
// Required environment variables:
|
||||
// - OPENAI_API_KEY: Your OpenAI API key for the test request
|
||||
func TestJsonParserPluginPerRequest(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
// Check if OpenAI API key is set
|
||||
if os.Getenv("OPENAI_API_KEY") == "" {
|
||||
t.Skip("OPENAI_API_KEY is not set, skipping per-request test")
|
||||
}
|
||||
|
||||
// Initialize the JSON parser plugin for per-request usage
|
||||
plugin, err := Init(PluginConfig{
|
||||
Usage: PerRequest,
|
||||
CleanupInterval: 5 * time.Minute,
|
||||
MaxAge: 30 * time.Minute,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error initializing JSON parser plugin: %v", err)
|
||||
}
|
||||
|
||||
account := BaseAccount{}
|
||||
|
||||
// Initialize Bifrost with the plugin
|
||||
client, err := bifrost.Init(ctx, schemas.BifrostConfig{
|
||||
Account: &account,
|
||||
LLMPlugins: []schemas.LLMPlugin{plugin},
|
||||
Logger: bifrost.NewDefaultLogger(schemas.LogLevelDebug),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error initializing Bifrost: %v", err)
|
||||
}
|
||||
defer client.Shutdown()
|
||||
|
||||
// Test request with plugin enabled via context
|
||||
var responseFormat interface{} = map[string]interface{}{
|
||||
"type": "json_object",
|
||||
}
|
||||
|
||||
request := &schemas.BifrostChatRequest{
|
||||
Provider: schemas.OpenAI,
|
||||
Model: "gpt-4o-mini",
|
||||
Input: []schemas.ChatMessage{
|
||||
{
|
||||
Role: schemas.ChatMessageRoleUser,
|
||||
Content: &schemas.ChatMessageContent{
|
||||
ContentStr: bifrost.Ptr("Return a JSON object with name and age fields."),
|
||||
},
|
||||
},
|
||||
},
|
||||
Params: &schemas.ChatParameters{
|
||||
ResponseFormat: &responseFormat,
|
||||
},
|
||||
}
|
||||
|
||||
// Create context with plugin enabled
|
||||
newContext := schemas.NewBifrostContext(context.Background(), schemas.NoDeadline).WithValue(EnableStreamingJSONParser, true)
|
||||
|
||||
// Make the streaming request
|
||||
responseChan, bifrostErr := client.ChatCompletionStreamRequest(newContext, request)
|
||||
|
||||
if bifrostErr != nil {
|
||||
t.Logf("Error in Bifrost request: %v", bifrostErr)
|
||||
}
|
||||
|
||||
// Process streaming responses
|
||||
if responseChan != nil {
|
||||
t.Logf("Streaming response channel received for per-request test")
|
||||
|
||||
// Read from the channel to see the streaming responses
|
||||
responseCount := 0
|
||||
|
||||
for streamResponse := range responseChan {
|
||||
responseCount++
|
||||
|
||||
if streamResponse.BifrostError != nil {
|
||||
t.Logf("Streaming response error: %v", streamResponse.BifrostError)
|
||||
}
|
||||
|
||||
if streamResponse.BifrostChatResponse != nil {
|
||||
for _, choice := range streamResponse.BifrostChatResponse.Choices {
|
||||
if choice.ChatStreamResponseChoice != nil && choice.ChatStreamResponseChoice.Delta.Content != nil {
|
||||
content := *choice.ChatStreamResponseChoice.Delta.Content
|
||||
if content != "" {
|
||||
t.Logf("Per-request chunk %d: %s", responseCount, content)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
t.Logf("Per-request stream completed after %d responses", responseCount)
|
||||
} else {
|
||||
t.Logf("No streaming response channel received for per-request test")
|
||||
}
|
||||
|
||||
t.Log("Per-request test completed - check logs for JSON parsing behavior")
|
||||
}
|
||||
|
||||
func TestParsePartialJSON(t *testing.T) {
|
||||
plugin, err := Init(PluginConfig{
|
||||
Usage: AllRequests,
|
||||
CleanupInterval: 5 * time.Minute,
|
||||
MaxAge: 30 * time.Minute,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error initializing JSON parser plugin: %v", err)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "Already valid JSON object",
|
||||
input: `{"name": "John", "age": 30}`,
|
||||
expected: `{"name": "John", "age": 30}`,
|
||||
},
|
||||
{
|
||||
name: "Partial JSON object missing closing brace",
|
||||
input: `{"name": "John", "age": 30, "city": "New York"`,
|
||||
expected: `{"name": "John", "age": 30, "city": "New York"}`,
|
||||
},
|
||||
{
|
||||
name: "Partial JSON array missing closing bracket",
|
||||
input: `["apple", "banana", "cherry"`,
|
||||
expected: `["apple", "banana", "cherry"]`,
|
||||
},
|
||||
{
|
||||
name: "Nested partial JSON",
|
||||
input: `{"user": {"name": "John", "details": {"age": 30, "city": "NY"`,
|
||||
expected: `{"user": {"name": "John", "details": {"age": 30, "city": "NY"}}}`,
|
||||
},
|
||||
{
|
||||
name: "Partial JSON with string containing newline",
|
||||
input: `{"message": "Hello\nWorld"`,
|
||||
expected: `{"message": "Hello\nWorld"}`,
|
||||
},
|
||||
{
|
||||
name: "Empty string",
|
||||
input: "",
|
||||
expected: "{}",
|
||||
},
|
||||
{
|
||||
name: "Whitespace only",
|
||||
input: " \n\t ",
|
||||
expected: "{}",
|
||||
},
|
||||
{
|
||||
name: "Non-JSON string",
|
||||
input: "This is not JSON",
|
||||
expected: "This is not JSON",
|
||||
},
|
||||
{
|
||||
name: "Partial JSON with escaped quotes",
|
||||
input: `{"message": "He said \"Hello\""`,
|
||||
expected: `{"message": "He said \"Hello\""}`,
|
||||
},
|
||||
{
|
||||
name: "Complex nested structure",
|
||||
input: `{"data": {"users": [{"id": 1, "name": "John"}, {"id": 2, "name": "Jane"`,
|
||||
expected: `{"data": {"users": [{"id": 1, "name": "John"}, {"id": 2, "name": "Jane"}]}}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := plugin.parsePartialJSON(tt.input)
|
||||
if result != tt.expected {
|
||||
t.Errorf("parsePartialJSON(%q) = %q, want %q", tt.input, result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
323
plugins/jsonparser/utils.go
Normal file
323
plugins/jsonparser/utils.go
Normal file
@@ -0,0 +1,323 @@
|
||||
package jsonparser
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/maximhq/bifrost/core/schemas"
|
||||
)
|
||||
|
||||
// getRequestID extracts a unique identifier for the request to maintain state
|
||||
func (p *JsonParserPlugin) getRequestID(ctx *schemas.BifrostContext, result *schemas.BifrostResponse) string {
|
||||
|
||||
// Try to get from result
|
||||
if result != nil && result.ChatResponse != nil && result.ChatResponse.ID != "" {
|
||||
return result.ChatResponse.ID
|
||||
}
|
||||
|
||||
// Try to get from context if not available in result
|
||||
if ctx != nil {
|
||||
if requestID, ok := ctx.Value(schemas.BifrostContextKeyRequestID).(string); ok && requestID != "" {
|
||||
return requestID
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// shouldRun determines if the plugin should process the request based on usage type
|
||||
func (p *JsonParserPlugin) shouldRun(ctx *schemas.BifrostContext, requestType schemas.RequestType) bool {
|
||||
// Run only for chat completion stream requests
|
||||
if requestType != schemas.ChatCompletionStreamRequest {
|
||||
return false
|
||||
}
|
||||
|
||||
switch p.usage {
|
||||
case AllRequests:
|
||||
return true
|
||||
case PerRequest:
|
||||
// Check if the context contains the plugin-specific key
|
||||
if ctx != nil {
|
||||
if value, ok := ctx.Value(EnableStreamingJSONParser).(bool); ok {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return false
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// accumulateContent adds new content to the accumulated content for a specific request
|
||||
func (p *JsonParserPlugin) accumulateContent(requestID, newContent string) string {
|
||||
p.mutex.Lock()
|
||||
defer p.mutex.Unlock()
|
||||
|
||||
// Get existing accumulated content
|
||||
existing := p.accumulatedContent[requestID]
|
||||
|
||||
if existing != nil {
|
||||
// Append to existing builder
|
||||
existing.Content.WriteString(newContent)
|
||||
return existing.Content.String()
|
||||
} else {
|
||||
// Create new builder
|
||||
builder := &strings.Builder{}
|
||||
builder.WriteString(newContent)
|
||||
p.accumulatedContent[requestID] = &AccumulatedContent{
|
||||
Content: builder,
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
return builder.String()
|
||||
}
|
||||
}
|
||||
|
||||
// parsePartialJSON parses a JSON string that may be missing closing braces
|
||||
func (p *JsonParserPlugin) parsePartialJSON(s string) string {
|
||||
// Trim whitespace
|
||||
s = strings.TrimSpace(s)
|
||||
if s == "" {
|
||||
return "{}"
|
||||
}
|
||||
|
||||
// Quick check: if it starts with { or [, it might be JSON
|
||||
if s[0] != '{' && s[0] != '[' {
|
||||
return s
|
||||
}
|
||||
|
||||
// First, try to parse the string as-is (fast path)
|
||||
if p.isValidJSON(s) {
|
||||
return s
|
||||
}
|
||||
|
||||
// Use a more efficient approach: build the completion directly
|
||||
return p.completeJSON(s)
|
||||
}
|
||||
|
||||
// completeJSON completes partial JSON with O(n) time complexity
|
||||
func (p *JsonParserPlugin) completeJSON(s string) string {
|
||||
// Pre-allocate buffer with estimated capacity
|
||||
capacity := len(s) + 10 // Estimate max 10 closing characters needed
|
||||
result := make([]byte, 0, capacity)
|
||||
|
||||
var stack []byte
|
||||
inString := false
|
||||
escaped := false
|
||||
|
||||
// Process the string once
|
||||
for i := 0; i < len(s); i++ {
|
||||
char := s[i]
|
||||
result = append(result, char)
|
||||
|
||||
if escaped {
|
||||
escaped = false
|
||||
continue
|
||||
}
|
||||
|
||||
if char == '\\' {
|
||||
escaped = true
|
||||
continue
|
||||
}
|
||||
|
||||
if char == '"' {
|
||||
inString = !inString
|
||||
continue
|
||||
}
|
||||
|
||||
if inString {
|
||||
continue
|
||||
}
|
||||
|
||||
switch char {
|
||||
case '{', '[':
|
||||
if char == '{' {
|
||||
stack = append(stack, '}')
|
||||
} else {
|
||||
stack = append(stack, ']')
|
||||
}
|
||||
case '}', ']':
|
||||
if len(stack) > 0 && stack[len(stack)-1] == char {
|
||||
stack = stack[:len(stack)-1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close any unclosed strings
|
||||
if inString {
|
||||
if escaped {
|
||||
// Remove the trailing backslash
|
||||
if len(result) > 0 {
|
||||
result = result[:len(result)-1]
|
||||
}
|
||||
}
|
||||
result = append(result, '"')
|
||||
}
|
||||
|
||||
// Add closing characters in reverse order
|
||||
for i := len(stack) - 1; i >= 0; i-- {
|
||||
result = append(result, stack[i])
|
||||
}
|
||||
|
||||
// Validate the result
|
||||
if p.isValidJSON(string(result)) {
|
||||
return string(result)
|
||||
}
|
||||
|
||||
// If still invalid, try progressive truncation (but more efficiently)
|
||||
return p.progressiveTruncation(s, result)
|
||||
}
|
||||
|
||||
// progressiveTruncation efficiently tries different truncation points
|
||||
func (p *JsonParserPlugin) progressiveTruncation(original string, completed []byte) string {
|
||||
// Try removing characters from the end until we get valid JSON
|
||||
// Use binary search for better performance
|
||||
left, right := 0, len(completed)
|
||||
|
||||
for left < right {
|
||||
mid := (left + right) / 2
|
||||
candidate := completed[:mid]
|
||||
|
||||
if p.isValidJSON(string(candidate)) {
|
||||
left = mid + 1
|
||||
} else {
|
||||
right = mid
|
||||
}
|
||||
}
|
||||
|
||||
// Try the best candidate
|
||||
if left > 0 && p.isValidJSON(string(completed[:left-1])) {
|
||||
return string(completed[:left-1])
|
||||
}
|
||||
|
||||
// Fallback to original
|
||||
return original
|
||||
}
|
||||
|
||||
// isValidJSON checks if a string is valid JSON
|
||||
func (p *JsonParserPlugin) isValidJSON(s string) bool {
|
||||
// Trim whitespace
|
||||
s = strings.TrimSpace(s)
|
||||
|
||||
// Empty string after trimming is not valid JSON
|
||||
if s == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
return json.Valid([]byte(s))
|
||||
}
|
||||
|
||||
// DEEP COPY METHODS
|
||||
|
||||
// deepCopyBifrostResponse creates a deep copy of BifrostResponse to avoid modifying the original
|
||||
func (p *JsonParserPlugin) deepCopyBifrostResponse(original *schemas.BifrostResponse) *schemas.BifrostResponse {
|
||||
if original == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a new BifrostResponse
|
||||
result := &schemas.BifrostResponse{}
|
||||
|
||||
// Copy ChatResponse if it exists (this is what we're interested in for the JSON parser)
|
||||
if original.ChatResponse != nil {
|
||||
result.ChatResponse = p.deepCopyBifrostChatResponse(original.ChatResponse)
|
||||
}
|
||||
|
||||
// Copy other response types if they exist (shallow copy since we don't modify them)
|
||||
result.TextCompletionResponse = original.TextCompletionResponse
|
||||
result.ResponsesResponse = original.ResponsesResponse
|
||||
result.ResponsesStreamResponse = original.ResponsesStreamResponse
|
||||
result.EmbeddingResponse = original.EmbeddingResponse
|
||||
result.SpeechResponse = original.SpeechResponse
|
||||
result.SpeechStreamResponse = original.SpeechStreamResponse
|
||||
result.TranscriptionResponse = original.TranscriptionResponse
|
||||
result.TranscriptionStreamResponse = original.TranscriptionStreamResponse
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// deepCopyBifrostChatResponse creates a deep copy of BifrostChatResponse
|
||||
func (p *JsonParserPlugin) deepCopyBifrostChatResponse(original *schemas.BifrostChatResponse) *schemas.BifrostChatResponse {
|
||||
if original == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
result := &schemas.BifrostChatResponse{
|
||||
ID: original.ID,
|
||||
Created: original.Created,
|
||||
Model: original.Model,
|
||||
Object: original.Object,
|
||||
ServiceTier: original.ServiceTier,
|
||||
SystemFingerprint: original.SystemFingerprint,
|
||||
Usage: original.Usage, // Shallow copy - usage shouldn't be modified
|
||||
ExtraFields: original.ExtraFields, // Shallow copy
|
||||
}
|
||||
|
||||
// Deep copy Choices slice
|
||||
if original.Choices != nil {
|
||||
result.Choices = make([]schemas.BifrostResponseChoice, len(original.Choices))
|
||||
for i, choice := range original.Choices {
|
||||
result.Choices[i] = p.deepCopyBifrostResponseChoice(choice)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// deepCopyBifrostResponseChoice creates a deep copy of BifrostResponseChoice
|
||||
func (p *JsonParserPlugin) deepCopyBifrostResponseChoice(original schemas.BifrostResponseChoice) schemas.BifrostResponseChoice {
|
||||
result := schemas.BifrostResponseChoice{
|
||||
Index: original.Index,
|
||||
FinishReason: original.FinishReason,
|
||||
LogProbs: original.LogProbs,
|
||||
}
|
||||
|
||||
// Deep copy ChatStreamResponseChoice if it exists (this is what we modify)
|
||||
if original.ChatStreamResponseChoice != nil {
|
||||
result.ChatStreamResponseChoice = p.deepCopyChatStreamResponseChoice(original.ChatStreamResponseChoice)
|
||||
}
|
||||
|
||||
// Shallow copy other choice types since we don't modify them
|
||||
result.ChatNonStreamResponseChoice = original.ChatNonStreamResponseChoice
|
||||
result.TextCompletionResponseChoice = original.TextCompletionResponseChoice
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// deepCopyChatStreamResponseChoice creates a deep copy of ChatStreamResponseChoice
|
||||
func (p *JsonParserPlugin) deepCopyChatStreamResponseChoice(original *schemas.ChatStreamResponseChoice) *schemas.ChatStreamResponseChoice {
|
||||
if original == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
result := &schemas.ChatStreamResponseChoice{}
|
||||
|
||||
// Deep copy Delta pointer if it exists
|
||||
if original.Delta != nil {
|
||||
result.Delta = p.deepCopyChatStreamResponseChoiceDelta(original.Delta)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// deepCopyChatStreamResponseChoiceDelta creates a deep copy of ChatStreamResponseChoiceDelta
|
||||
func (p *JsonParserPlugin) deepCopyChatStreamResponseChoiceDelta(original *schemas.ChatStreamResponseChoiceDelta) *schemas.ChatStreamResponseChoiceDelta {
|
||||
if original == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
result := &schemas.ChatStreamResponseChoiceDelta{
|
||||
Role: original.Role,
|
||||
Reasoning: original.Reasoning, // Shallow copy
|
||||
Refusal: original.Refusal, // Shallow copy
|
||||
ToolCalls: original.ToolCalls, // Shallow copy - we don't modify tool calls
|
||||
}
|
||||
|
||||
// Deep copy Content pointer if it exists (this is what we modify)
|
||||
if original.Content != nil {
|
||||
contentCopy := *original.Content
|
||||
result.Content = &contentCopy
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
1
plugins/jsonparser/version
Normal file
1
plugins/jsonparser/version
Normal file
@@ -0,0 +1 @@
|
||||
1.5.4
|
||||
Reference in New Issue
Block a user