From f336eba19a0c19d4e27f505fb4378fd0724e33ce Mon Sep 17 00:00:00 2001 From: Sakurasan <1173092237@qq.com> Date: Sun, 3 Dec 2023 15:32:24 +0800 Subject: [PATCH 1/7] update go.mod --- go.mod | 49 +++++++++++++++++++++++++------------------------ go.sum | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 24 deletions(-) diff --git a/go.mod b/go.mod index 0661027..ab27f47 100644 --- a/go.mod +++ b/go.mod @@ -4,56 +4,57 @@ go 1.20 require ( github.com/Sakurasan/to v0.0.0-20180919163141-e72657dd7c7d - github.com/duke-git/lancet/v2 v2.2.3 + github.com/duke-git/lancet/v2 v2.2.7 github.com/faiface/beep v1.1.0 github.com/gin-gonic/gin v1.9.1 - github.com/glebarez/sqlite v1.8.0 + github.com/glebarez/sqlite v1.10.0 github.com/golang-jwt/jwt v3.2.2+incompatible - github.com/google/uuid v1.3.0 + github.com/google/uuid v1.4.0 github.com/patrickmn/go-cache v2.1.0+incompatible - github.com/pkoukk/tiktoken-go v0.1.4 - github.com/sashabaranov/go-openai v1.13.0 + github.com/pkoukk/tiktoken-go v0.1.6 + github.com/sashabaranov/go-openai v1.17.9 gopkg.in/vansante/go-ffprobe.v2 v2.1.1 - gorm.io/gorm v1.25.2 + gorm.io/gorm v1.25.5 ) require ( - github.com/bytedance/sonic v1.9.2 // indirect - github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/bytedance/sonic v1.10.2 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect + github.com/chenzhuoyu/iasm v0.9.1 // indirect github.com/dlclark/regexp2 v1.10.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect - github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/glebarez/go-sqlite v1.21.2 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.14.1 // indirect + github.com/go-playground/validator/v10 v10.16.0 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/google/go-cmp v0.5.9 // indirect - github.com/hajimehoshi/go-mp3 v0.3.0 // indirect + github.com/hajimehoshi/go-mp3 v0.3.4 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/cpuid/v2 v2.2.5 // indirect + github.com/klauspost/cpuid/v2 v2.2.6 // indirect github.com/leodido/go-urn v1.2.4 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/ugorji/go/codec v1.2.11 // indirect - golang.org/x/arch v0.4.0 // indirect - golang.org/x/crypto v0.11.0 // indirect - golang.org/x/exp v0.0.0-20221208152030-732eee02a75a // indirect - golang.org/x/net v0.13.0 // indirect - golang.org/x/sys v0.10.0 // indirect - golang.org/x/text v0.11.0 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + golang.org/x/arch v0.6.0 // indirect + golang.org/x/crypto v0.16.0 // indirect + golang.org/x/exp v0.0.0-20231127185646-65229373498e // indirect + golang.org/x/net v0.19.0 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/text v0.14.0 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - modernc.org/libc v1.24.1 // indirect + modernc.org/libc v1.35.0 // indirect modernc.org/mathutil v1.6.0 // indirect - modernc.org/memory v1.6.0 // indirect - modernc.org/sqlite v1.23.1 // indirect + modernc.org/memory v1.7.2 // indirect + modernc.org/sqlite v1.27.0 // indirect ) diff --git a/go.sum b/go.sum index ff24e7f..7f5467b 100644 --- a/go.sum +++ b/go.sum @@ -4,9 +4,17 @@ github.com/Sakurasan/to v0.0.0-20180919163141-e72657dd7c7d/go.mod h1:2sp0vsMyh5s github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.9.2 h1:GDaNjuWSGu09guE9Oql0MSTNhNCLlWwO8y/xM5BzcbM= github.com/bytedance/sonic v1.9.2/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= +github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE= +github.com/bytedance/sonic v1.10.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= +github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= +github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0= +github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= github.com/d4l3k/messagediff v1.2.2-0.20190829033028-7e0a312ae40b/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -15,12 +23,16 @@ github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/duke-git/lancet/v2 v2.2.3 h1:Lj4iWgvEbgktEjAfqxE1G2BoGm1mL7l3QHBlXRYptjE= github.com/duke-git/lancet/v2 v2.2.3/go.mod h1:zGa2R4xswg6EG9I6WnyubDbFO/+A/RROxIbXcwryTsc= +github.com/duke-git/lancet/v2 v2.2.7 h1:u9zr6HR+MDUvZEtTlAFtSTIgZfEFsN7cKi27n5weZsw= +github.com/duke-git/lancet/v2 v2.2.7/go.mod h1:zGa2R4xswg6EG9I6WnyubDbFO/+A/RROxIbXcwryTsc= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/faiface/beep v1.1.0 h1:A2gWP6xf5Rh7RG/p9/VAW2jRSDEGQm5sbOb38sf5d4c= github.com/faiface/beep v1.1.0/go.mod h1:6I8p6kK2q4opL/eWb+kAkk38ehnTunWeToJB+s51sT4= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= @@ -31,6 +43,8 @@ github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9g github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k= github.com/glebarez/sqlite v1.8.0 h1:02X12E2I/4C1n+v90yTqrjRa8yuo7c3KeHI3FRznCvc= github.com/glebarez/sqlite v1.8.0/go.mod h1:bpET16h1za2KOOMb8+jCp6UBP/iahDpfPQqSaYLTLx8= +github.com/glebarez/sqlite v1.10.0 h1:u4gt8y7OND/cCei/NMHmfbLxF6xP2wgKcT/BJf2pYkc= +github.com/glebarez/sqlite v1.10.0/go.mod h1:IJ+lfSOmiekhQsFTJRx/lHtGYmCdtAiTaf5wI9u5uHA= github.com/go-audio/audio v1.0.0/go.mod h1:6uAu0+H2lHkwdGsAY+j2wHPNPpPoeg5AaEFh9FlA+Zs= github.com/go-audio/riff v1.0.0/go.mod h1:l3cQwc85y79NQFCRB7TiPoNiaijp6q8Z0Uv38rVG498= github.com/go-audio/wav v1.0.0/go.mod h1:3yoReyQOsiARkvPl3ERCi8JFjihzG6WhjYpZCf5zAWE= @@ -41,6 +55,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k= github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE= +github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= @@ -53,10 +69,15 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hajimehoshi/go-mp3 v0.3.0 h1:fTM5DXjp/DL2G74HHAs/aBGiS9Tg7wnp+jkU38bHy4g= github.com/hajimehoshi/go-mp3 v0.3.0/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM= +github.com/hajimehoshi/go-mp3 v0.3.4 h1:NUP7pBYH8OguP4diaTZ9wJbUbk3tC0KlfzsEpWmYj68= +github.com/hajimehoshi/go-mp3 v0.3.4/go.mod h1:fRtZraRFcWb0pu7ok0LqyFhCUrPeMsGRSVop0eemFmo= github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI= github.com/hajimehoshi/oto v0.7.1/go.mod h1:wovJ8WWMfFKvP587mhHgot/MBr4DnNy9m6EepeVGnos= +github.com/hajimehoshi/oto/v2 v2.3.1/go.mod h1:seWLbgHH7AyUMYKfKYT9pg7PhUu9/SisyJvNTT+ASQo= github.com/icza/bitio v1.0.0/go.mod h1:0jGnlLAx8MKMr9VGnn/4YrvZiprkvBelsVIbA9Jjr9A= github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6/go.mod h1:xQig96I1VNBDIWGCdTt54nHt6EeI639SmHycLYL7FkA= github.com/jfreymuth/oggvorbis v1.0.1/go.mod h1:NqS+K+UXKje0FUYUPosyQ+XTVvjmVjps1aEZH1sumIk= @@ -70,11 +91,16 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= +github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= 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/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mewkiz/flac v1.0.7/go.mod h1:yU74UH277dBUpqxPouHSQIar3G1X/QIclVbFahSd1pU= github.com/mewkiz/pkg v0.0.0-20190919212034-518ade7978e2/go.mod h1:3E2FUC/qYUfM8+r9zAwpeHJzqRVVMIYnpzD/clwWxyA= @@ -87,17 +113,23 @@ github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaR github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= +github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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.4 h1:bniMzWdUvNO6YkRbASo2x5qJf2LAG/TIJojqz+Igm8E= github.com/pkoukk/tiktoken-go v0.1.4/go.mod h1:9NiV+i9mJKGj1rYOT+njbv+ZwA/zJxYdewGl6qVatpg= +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/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/sashabaranov/go-openai v1.13.0 h1:EAusFfnhaMaaUspUZ2+MbB/ZcVeD4epJmTOlZ+8AcAE= github.com/sashabaranov/go-openai v1.13.0/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg= +github.com/sashabaranov/go-openai v1.17.9 h1:QEoBiGKWW68W79YIfXWEFZ7l5cEgZBV4/Ow3uy+5hNY= +github.com/sashabaranov/go-openai v1.17.9/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg= 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= @@ -109,34 +141,50 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 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/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc= golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.6.0 h1:S0JTfE48HbRj80+4tbvZDYsJ3tGv6BUU3XxyZ7CirAc= +golang.org/x/arch v0.6.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= +golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= +golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20221208152030-732eee02a75a h1:4iLhBPcpqFmylhnkbY3W0ONLUYYkDAW9xMFLfxgsvCw= golang.org/x/exp v0.0.0-20221208152030-732eee02a75a/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20231127185646-65229373498e h1:Gvh4YaCaXNs6dKTlfgismwWZKyjVZXwOPfIyUaqU3No= +golang.org/x/exp v0.0.0-20231127185646-65229373498e/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/image v0.0.0-20190220214146-31aff87c08e9/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.13.0 h1:Nvo8UFsZ8X3BhAC9699Z1j7XQ3rsZnUUm7jfBEk1ueY= golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= @@ -150,12 +198,21 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/gorm v1.25.2 h1:gs1o6Vsa+oVKG/a9ElL3XgyGfghFfkKA2SInQaCyMho= gorm.io/gorm v1.25.2/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= +gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls= +gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= modernc.org/libc v1.24.1 h1:uvJSeCKL/AgzBo2yYIPPTy82v21KgGnizcGYfBHaNuM= modernc.org/libc v1.24.1/go.mod h1:FmfO1RLrU3MHJfyi9eYYmZBfi/R+tqZ6+hQ3yQQUkak= +modernc.org/libc v1.35.0 h1:EQ4szx6Q/QLZuysmAnI4dfRnKbAbNlENp23ruvTJ2nE= +modernc.org/libc v1.35.0/go.mod h1:YAXkAZ8ktnkCKaN9sw/UDeUVkGYJ/YquGO4FTi5nmHE= modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= modernc.org/memory v1.6.0 h1:i6mzavxrE9a30whzMfwf7XWVODx2r5OYXvU46cirX7o= modernc.org/memory v1.6.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= +modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E= +modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E= modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM= modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk= +modernc.org/sqlite v1.27.0 h1:MpKAHoyYB7xqcwnUwkuD+npwEa0fojF0B5QRbN+auJ8= +modernc.org/sqlite v1.27.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= From b61e85b7fcc404cf6d4b82fb455b9e5b48bd5038 Mon Sep 17 00:00:00 2001 From: Sakurasan <26715255+Sakurasan@users.noreply.github.com> Date: Tue, 16 Apr 2024 23:47:06 +0800 Subject: [PATCH 2/7] refact: openai,claude --- pkg/claude/chat.go | 290 ++++++++++++++++++++++++++++++++++++++++ pkg/claude/claude.go | 22 ++- pkg/openai/chat.go | 309 +++++++++++++++++++++++++++++++++++++++++++ router/chat.go | 29 ++++ router/router.go | 3 + 5 files changed, 650 insertions(+), 3 deletions(-) create mode 100644 pkg/claude/chat.go create mode 100644 pkg/openai/chat.go create mode 100644 router/chat.go diff --git a/pkg/claude/chat.go b/pkg/claude/chat.go new file mode 100644 index 0000000..7532fb2 --- /dev/null +++ b/pkg/claude/chat.go @@ -0,0 +1,290 @@ +// https://docs.anthropic.com/claude/reference/messages_post + +package claude + +import ( + "bufio" + "bytes" + "encoding/json" + "fmt" + "io" + "log" + "net/http" + "opencatd-open/pkg/openai" + "opencatd-open/pkg/tokenizer" + "opencatd-open/store" + "strings" + + "github.com/gin-gonic/gin" +) + +func ChatProxy(c *gin.Context, chatReq *openai.ChatCompletionRequest) { + ChatMessages(c, chatReq) +} + +func ChatTextCompletions(c *gin.Context, chatReq *openai.ChatCompletionRequest) { + +} + +type ClaudeRequest struct { + Model string `json:"model,omitempty"` + Messages any `json:"messages,omitempty"` + MaxTokens int `json:"max_tokens,omitempty"` + Stream bool `json:"stream,omitempty"` + System string `json:"system,omitempty"` + TopK int `json:"top_k,omitempty"` + TopP float64 `json:"top_p,omitempty"` + Temperature float64 `json:"temperature,omitempty"` +} + +func (c *ClaudeRequest) ByteJson() []byte { + bytejson, _ := json.Marshal(c) + return bytejson +} + +type ClaudeMessages struct { + Role string `json:"role,omitempty"` + Content string `json:"content,omitempty"` +} + +type VisionMessages struct { + Role string `json:"role,omitempty"` + Content []ClaudeContent `json:"content,omitempty"` +} + +type ClaudeContent struct { + Type string `json:"type,omitempty"` + Source *ClaudeSource `json:"source,omitempty"` + Text string `json:"text,omitempty"` +} + +type ClaudeSource struct { + Type string `json:"type,omitempty"` + MediaType string `json:"media_type,omitempty"` + Data string `json:"data,omitempty"` +} + +type ClaudeResponse struct { + ID string `json:"id"` + Type string `json:"type"` + Role string `json:"role"` + Model string `json:"model"` + StopSequence any `json:"stop_sequence"` + Usage struct { + InputTokens int `json:"input_tokens"` + OutputTokens int `json:"output_tokens"` + } `json:"usage"` + Content []struct { + Type string `json:"type"` + Text string `json:"text"` + } `json:"content"` + StopReason string `json:"stop_reason"` +} + +type ClaudeStreamResponse struct { + Type string `json:"type"` + Index int `json:"index"` + ContentBlock struct { + Type string `json:"type"` + Text string `json:"text"` + } `json:"content_block"` + Delta struct { + Type string `json:"type"` + Text string `json:"text"` + StopReason string `json:"stop_reason"` + StopSequence any `json:"stop_sequence"` + } `json:"delta"` + Message struct { + ID string `json:"id"` + Type string `json:"type"` + Role string `json:"role"` + Content []any `json:"content"` + Model string `json:"model"` + StopReason string `json:"stop_reason"` + StopSequence any `json:"stop_sequence"` + Usage struct { + InputTokens int `json:"input_tokens"` + OutputTokens int `json:"output_tokens"` + } `json:"usage"` + } `json:"message"` + Error struct { + Type string `json:"type"` + Message string `json:"message"` + } `json:"error"` + Usage struct { + OutputTokens int `json:"output_tokens"` + } `json:"usage"` +} + +func ChatMessages(c *gin.Context, chatReq *openai.ChatCompletionRequest) { + // var haveImages bool + + usagelog := store.Tokens{Model: chatReq.Model} + var claudReq ClaudeRequest + claudReq.Model = chatReq.Model + claudReq.Stream = chatReq.Stream + claudReq.Temperature = chatReq.Temperature + claudReq.TopP = chatReq.TopP + claudReq.MaxTokens = 4096 + + var msgs []any + var prompt string + for _, msg := range chatReq.Messages { + if msg.Role == "system" { + claudReq.System = string(msg.Content) + continue + } + + var visioncontent []openai.VisionContent + if err := json.Unmarshal(msg.Content, &visioncontent); err != nil { + prompt += "<" + msg.Role + ">: " + string(msg.Content) + "\n" + + var claudemsgs ClaudeMessages + claudemsgs.Role = msg.Role + claudemsgs.Content = string(msg.Content) + msgs = append(msgs, claudemsgs) + + } else { + if len(visioncontent) > 0 { + var visionMessage VisionMessages + visionMessage.Role = msg.Role + + for _, content := range visioncontent { + var claudecontent []ClaudeContent + if content.Type == "text" { + prompt += "<" + msg.Role + ">: " + content.Text + "\n" + claudecontent = append(claudecontent, ClaudeContent{Type: "text", Text: content.Text}) + } else if content.Type == "image_url" { + if strings.HasPrefix(content.ImageURL.URL, "http") { + fmt.Println("链接:", content.ImageURL.URL) + } else if strings.HasPrefix(content.ImageURL.URL, "data:image") { + fmt.Println("base64:", content.ImageURL.URL[:20]) + } + // todo image tokens + var mediaType string + if strings.HasPrefix(content.ImageURL.URL, "data:image/jpeg") { + mediaType = "image/jpeg" + } + if strings.HasPrefix(content.ImageURL.URL, "data:image/png") { + mediaType = "image/png" + } + claudecontent = append(claudecontent, ClaudeContent{Type: "image", Source: &ClaudeSource{Type: "base64", MediaType: mediaType, Data: strings.Split(content.ImageURL.URL, ",")[1]}}) + // haveImages = true + + } + visionMessage.Content = claudecontent + } + msgs = append(msgs, visionMessage) + } + } + claudReq.Messages = msgs + + // if len(chatReq.Tools) > 0 { + // tooljson, _ := json.Marshal(chatReq.Tools) + // prompt += ": " + string(tooljson) + "\n" + // } + } + + usagelog.PromptCount = tokenizer.NumTokensFromStr(prompt, chatReq.Model) + + req, _ := http.NewRequest("POST", MessageEndpoint, strings.NewReader(fmt.Sprintf("%v", bytes.NewReader(claudReq.ByteJson())))) + client := http.DefaultClient + rsp, err := client.Do(req) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + defer rsp.Body.Close() + if rsp.StatusCode != http.StatusOK { + io.Copy(c.Writer, rsp.Body) + return + } + + teeReader := io.TeeReader(rsp.Body, c.Writer) + + dataChan := make(chan string, 1) + // stopChan := make(chan bool) + + var result string + + scanner := bufio.NewScanner(teeReader) + + go func() { + for scanner.Scan() { + line := scanner.Bytes() + if len(line) > 0 && bytes.HasPrefix(line, []byte("data: ")) { + if bytes.HasPrefix(line, []byte("data: [DONE]")) { + dataChan <- string(line) + "\n" + break + } + var claudeResp ClaudeStreamResponse + line = bytes.Replace(line, []byte("data: "), []byte(""), -1) + line = bytes.TrimSpace(line) + if err := json.Unmarshal(line, &claudeResp); err != nil { + continue + } + + if claudeResp.Type == "message_start" { + if claudeResp.Message.Role != "" { + result += "<" + claudeResp.Message.Role + ">" + } + } else if claudeResp.Type == "message_stop" { + break + } + + if claudeResp.Delta.Text != "" { + result += claudeResp.Delta.Text + } + var choice openai.Choice + choice.Delta.Role = claudeResp.Message.Role + choice.Delta.Content = claudeResp.Delta.Text + choice.FinishReason = claudeResp.Delta.StopReason + + chatResp := openai.ChatCompletionStreamResponse{ + Model: chatReq.Model, + Choices: []openai.Choice{choice}, + } + dataChan <- "data: " + string(chatResp.ByteJson()) + "\n" + if claudeResp.Delta.StopReason != "" { + dataChan <- "\ndata: [DONE]\n" + } + } else { + if !bytes.HasPrefix(line, []byte("event:")) { + dataChan <- string(line) + "\n" + } + } + } + defer close(dataChan) + }() + + c.Writer.Header().Set("Content-Type", "text/event-stream") + c.Writer.Header().Set("Cache-Control", "no-cache") + c.Writer.Header().Set("Connection", "keep-alive") + c.Writer.Header().Set("Transfer-Encoding", "chunked") + c.Writer.Header().Set("X-Accel-Buffering", "no") + + c.Stream(func(w io.Writer) bool { + if data, ok := <-dataChan; ok { + if strings.HasPrefix(data, "data: ") { + c.Writer.WriteString(data) + // c.Writer.WriteString("\n\n") + } else { + c.Writer.WriteHeader(http.StatusBadGateway) + c.Writer.WriteString(data) + } + c.Writer.Flush() + return true + } + go func() { + usagelog.CompletionCount = tokenizer.NumTokensFromStr(result, chatReq.Model) + usagelog.Cost = fmt.Sprintf("%.6f", tokenizer.Cost(usagelog.Model, usagelog.PromptCount, usagelog.CompletionCount)) + if err := store.Record(&usagelog); err != nil { + log.Println(err) + } + if err := store.SumDaily(usagelog.UserID); err != nil { + log.Println(err) + } + }() + return false + }) +} diff --git a/pkg/claude/claude.go b/pkg/claude/claude.go index 8b4ae07..0964f46 100644 --- a/pkg/claude/claude.go +++ b/pkg/claude/claude.go @@ -22,11 +22,26 @@ data: {"completion":"","stop_reason":"stop_sequence","model":"claude-2.0","stop" # Model Pricing -Claude Instant |100,000 tokens |Prompt $1.63/million tokens |Completion $5.51/million tokens +Claude Instant |100,000 tokens |Prompt $1.63/million tokens |Completion $5.51/million tokens -Claude 2 |100,000 tokens |Prompt $11.02/million tokens |Completion $32.68/million tokens +Claude 2 |100,000 tokens |Prompt $11.02/million tokens |Completion $32.68/million tokens *Claude 1 is still accessible and offered at the same price as Claude 2. +# AWS +https://docs.aws.amazon.com/bedrock/latest/userguide/what-is-service.html +https://aws.amazon.com/cn/bedrock/pricing/ +Anthropic models Price for 1000 input tokens Price for 1000 output tokens +Claude Instant $0.00163 $0.00551 + +Claude $0.01102 $0.03268 + +https://docs.aws.amazon.com/bedrock/latest/userguide/endpointsTable.html +地区名称 地区 端点 协议 +美国东部(弗吉尼亚北部) 美国东部1 bedrock-runtime.us-east-1.amazonaws.com HTTPS + bedrock-runtime-fips.us-east-1.amazonaws.com HTTPS +美国西部(俄勒冈州) 美国西2号 bedrock-runtime.us-west-2.amazonaws.com HTTPS + bedrock-runtime-fips.us-west-2.amazonaws.com HTTPS +亚太地区(新加坡) ap-东南-1 bedrock-runtime.ap-southeast-1.amazonaws.com HTTPS */ // package anthropic @@ -53,7 +68,8 @@ import ( ) var ( - ClaudeUrl = "https://api.anthropic.com/v1/complete" + ClaudeUrl = "https://api.anthropic.com/v1/complete" + MessageEndpoint = "https://api.anthropic.com/v1/messages" ) type MessageModule struct { diff --git a/pkg/openai/chat.go b/pkg/openai/chat.go new file mode 100644 index 0000000..9b9f61e --- /dev/null +++ b/pkg/openai/chat.go @@ -0,0 +1,309 @@ +package openai + +import ( + "bufio" + "bytes" + "encoding/json" + "fmt" + "io" + "log" + "net/http" + "opencatd-open/pkg/tokenizer" + "opencatd-open/store" + "os" + "strings" + + "github.com/gin-gonic/gin" +) + +const ( + OpenAI_Endpoint = "https://api.openai.com/v1/chat/completions" +) + +var ( + BaseURL string // "https://api.openai.com" + AIGateWay_Endpoint = "https://gateway.ai.cloudflare.com/v1/431ba10f11200d544922fbca177aaa7f/openai/openai/chat/completions" +) + +func init() { + if os.Getenv("OpenAI_Endpoint") != "" { + BaseURL = os.Getenv("OpenAI_Endpoint") + } + if os.Getenv("AIGateWay_Endpoint") != "" { + AIGateWay_Endpoint = os.Getenv("AIGateWay_Endpoint") + } +} + +// Vision Content +type VisionContent struct { + Type string `json:"type,omitempty"` + Text string `json:"text,omitempty"` + ImageURL *VisionImageURL `json:"image_url,omitempty"` +} +type VisionImageURL struct { + URL string `json:"url,omitempty"` + Detail string `json:"detail,omitempty"` +} + +type ChatCompletionMessage struct { + Role string `json:"role"` + Content json.RawMessage `json:"content"` + Name string `json:"name,omitempty"` +} + +type FunctionDefinition struct { + Name string `json:"name"` + Description string `json:"description,omitempty"` + Parameters any `json:"parameters"` +} + +type Tool struct { + Type string `json:"type"` + Function *FunctionDefinition `json:"function,omitempty"` +} + +type ChatCompletionRequest struct { + Model string `json:"model"` + Messages []ChatCompletionMessage `json:"messages"` + MaxTokens int `json:"max_tokens,omitempty"` + Temperature float64 `json:"temperature,omitempty"` + TopP float64 `json:"top_p,omitempty"` + N int `json:"n,omitempty"` + Stream bool `json:"stream,omitempty"` + Stop []string `json:"stop,omitempty"` + PresencePenalty float64 `json:"presence_penalty,omitempty"` + FrequencyPenalty float64 `json:"frequency_penalty,omitempty"` + LogitBias map[string]int `json:"logit_bias,omitempty"` + User string `json:"user,omitempty"` + // Functions []FunctionDefinition `json:"functions,omitempty"` + // FunctionCall any `json:"function_call,omitempty"` + Tools []Tool `json:"tools,omitempty"` + // ToolChoice any `json:"tool_choice,omitempty"` +} + +func (c ChatCompletionRequest) ToByteJson() []byte { + bytejson, _ := json.Marshal(c) + return bytejson +} + +type ToolCall struct { + ID string `json:"id"` + Type string `json:"type"` + Function struct { + Name string `json:"name"` + Arguments string `json:"arguments"` + } `json:"function"` +} + +type ChatCompletionResponse struct { + ID string `json:"id"` + Object string `json:"object"` + Created int `json:"created"` + Model string `json:"model"` + Choices []struct { + Index int `json:"index"` + Message struct { + Role string `json:"role"` + Content string `json:"content"` + ToolCalls []ToolCall `json:"tool_calls"` + } `json:"message"` + Logprobs string `json:"logprobs"` + FinishReason string `json:"finish_reason"` + } `json:"choices"` + Usage struct { + PromptTokens int `json:"prompt_tokens"` + CompletionTokens int `json:"completion_tokens"` + TotalTokens int `json:"total_tokens"` + } `json:"usage"` + SystemFingerprint string `json:"system_fingerprint"` +} + +type Choice struct { + Index int `json:"index"` + Delta struct { + Role string `json:"role"` + Content string `json:"content"` + ToolCalls []ToolCall `json:"tool_calls"` + } `json:"delta"` + FinishReason string `json:"finish_reason"` + Usage struct { + PromptTokens int `json:"prompt_tokens"` + CompletionTokens int `json:"completion_tokens"` + TotalTokens int `json:"total_tokens"` + } `json:"usage"` +} + +type ChatCompletionStreamResponse struct { + ID string `json:"id"` + Object string `json:"object"` + Created int `json:"created"` + Model string `json:"model"` + Choices []Choice `json:"choices"` +} + +func (c *ChatCompletionStreamResponse) ByteJson() []byte { + bytejson, _ := json.Marshal(c) + return bytejson +} + +func ChatProxy(c *gin.Context, chatReq *ChatCompletionRequest) { + usagelog := store.Tokens{Model: chatReq.Model} + + token, _ := c.Get("localuser") + + lu, err := store.GetUserByToken(token.(string)) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": gin.H{ + "message": err.Error(), + }, + }) + return + } + usagelog.UserID = int(lu.ID) + + var prompt string + for _, msg := range chatReq.Messages { + // prompt += "<" + msg.Role + ">: " + msg.Content + "\n" + var visioncontent []VisionContent + if err := json.Unmarshal(msg.Content, &visioncontent); err != nil { + prompt += "<" + msg.Role + ">: " + string(msg.Content) + "\n" + } else { + if len(visioncontent) > 0 { + for _, content := range visioncontent { + if content.Type == "text" { + prompt += "<" + msg.Role + ">: " + content.Text + "\n" + } else if content.Type == "image_url" { + if strings.HasPrefix(content.ImageURL.URL, "http") { + fmt.Println("链接:", content.ImageURL.URL) + } else if strings.HasPrefix(content.ImageURL.URL, "data:image") { + fmt.Println("base64:", content.ImageURL.URL[:20]) + } + // todo image tokens + } + + } + + } + } + if len(chatReq.Tools) > 0 { + tooljson, _ := json.Marshal(chatReq.Tools) + prompt += ": " + string(tooljson) + "\n" + } + } + + usagelog.PromptCount = tokenizer.NumTokensFromStr(prompt, chatReq.Model) + + onekey, err := store.SelectKeyCache("openai") + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + var req *http.Request + + if onekey.EndPoint != "" { // 优先key的endpoint + req, err = http.NewRequest(c.Request.Method, onekey.EndPoint+c.Request.RequestURI, bytes.NewReader(chatReq.ToByteJson())) + } else { + if BaseURL != "" { // 其次BaseURL + req, err = http.NewRequest(c.Request.Method, BaseURL+c.Request.RequestURI, bytes.NewReader(chatReq.ToByteJson())) + } else { // 最后是gateway的endpoint + req, err = http.NewRequest(c.Request.Method, AIGateWay_Endpoint, bytes.NewReader(chatReq.ToByteJson())) + } + } + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + req.Header = c.Request.Header + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", onekey.Key)) + + resp, err := http.DefaultClient.Do(req) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + defer resp.Body.Close() + teeReader := io.TeeReader(resp.Body, c.Writer) + + var result string + if chatReq.Stream { + // 流式响应 + scanner := bufio.NewScanner(teeReader) + + for scanner.Scan() { + line := scanner.Bytes() + if len(line) > 0 && bytes.HasPrefix(line, []byte("data: ")) { + if bytes.HasPrefix(line, []byte("data: [DONE]")) { + break + } + var opiResp ChatCompletionStreamResponse + line = bytes.Replace(line, []byte("data: "), []byte(""), -1) + line = bytes.TrimSpace(line) + if err := json.Unmarshal(line, &opiResp); err != nil { + continue + } + + if opiResp.Choices != nil && len(opiResp.Choices) > 0 { + if opiResp.Choices[0].Delta.Role != "" { + result += "<" + opiResp.Choices[0].Delta.Role + "> " + } + result += opiResp.Choices[0].Delta.Content // 计算Content Token + + if len(opiResp.Choices[0].Delta.ToolCalls) > 0 { // 计算ToolCalls token + if opiResp.Choices[0].Delta.ToolCalls[0].Function.Name != "" { + result += "name:" + opiResp.Choices[0].Delta.ToolCalls[0].Function.Name + " arguments:" + } + result += opiResp.Choices[0].Delta.ToolCalls[0].Function.Arguments + } + } else { + continue + } + } + + } + } else { + // 处理非流式响应 + body, err := io.ReadAll(teeReader) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + var opiResp ChatCompletionResponse + if err := json.Unmarshal(body, &opiResp); err != nil { + log.Println("Error parsing JSON:", err) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Error parsing JSON," + err.Error()}) + return + } + if opiResp.Choices != nil && len(opiResp.Choices) > 0 { + if opiResp.Choices[0].Message.Role != "" { + result += "<" + opiResp.Choices[0].Message.Role + "> " + } + result += opiResp.Choices[0].Message.Content + + if len(opiResp.Choices[0].Message.ToolCalls) > 0 { + if opiResp.Choices[0].Message.ToolCalls[0].Function.Name != "" { + result += "name:" + opiResp.Choices[0].Message.ToolCalls[0].Function.Name + " arguments:" + } + result += opiResp.Choices[0].Message.ToolCalls[0].Function.Arguments + } + + } + } + usagelog.CompletionCount = tokenizer.NumTokensFromStr(result, chatReq.Model) + usagelog.Cost = fmt.Sprintf("%.6f", tokenizer.Cost(usagelog.Model, usagelog.PromptCount, usagelog.CompletionCount)) + if err := store.Record(&usagelog); err != nil { + log.Println(err) + } + if err := store.SumDaily(usagelog.UserID); err != nil { + log.Println(err) + } +} + +type ErrResponse struct { + Error struct { + Message string `json:"message"` + Code string `json:"code"` + } `json:"error"` +} diff --git a/router/chat.go b/router/chat.go new file mode 100644 index 0000000..f132428 --- /dev/null +++ b/router/chat.go @@ -0,0 +1,29 @@ +package router + +import ( + "net/http" + "strings" + + "opencatd-open/pkg/claude" + "opencatd-open/pkg/openai" + + "github.com/gin-gonic/gin" +) + +func ChatHandler(c *gin.Context) { + var chatreq openai.ChatCompletionRequest + if err := c.ShouldBindJSON(&chatreq); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + if strings.HasPrefix(chatreq.Model, "gpt") { + openai.ChatProxy(c, &chatreq) + return + } + + if strings.HasPrefix(chatreq.Model, "claude") { + claude.ChatProxy(c, &chatreq) + return + } +} diff --git a/router/router.go b/router/router.go index dd3b5fb..af2b2c1 100644 --- a/router/router.go +++ b/router/router.go @@ -499,6 +499,9 @@ func HandleProy(c *gin.Context) { return } + ChatHandler(c) + return + if err := c.BindJSON(&chatreq); err != nil { c.AbortWithError(http.StatusBadRequest, err) return From ec073db5c7781679174fc151314f9895cc18afc4 Mon Sep 17 00:00:00 2001 From: Sakurasan <26715255+Sakurasan@users.noreply.github.com> Date: Wed, 17 Apr 2024 01:21:47 +0800 Subject: [PATCH 3/7] refact: azure --- pkg/openai/chat.go | 41 +++++++++++++++++++++++++++++++---------- store/cache.go | 5 +++++ 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/pkg/openai/chat.go b/pkg/openai/chat.go index 9b9f61e..7d3ba6d 100644 --- a/pkg/openai/chat.go +++ b/pkg/openai/chat.go @@ -17,6 +17,7 @@ import ( ) const ( + AzureApiVersion = "2024-02-01" OpenAI_Endpoint = "https://api.openai.com/v1/chat/completions" ) @@ -202,23 +203,35 @@ func ChatProxy(c *gin.Context, chatReq *ChatCompletionRequest) { var req *http.Request - if onekey.EndPoint != "" { // 优先key的endpoint - req, err = http.NewRequest(c.Request.Method, onekey.EndPoint+c.Request.RequestURI, bytes.NewReader(chatReq.ToByteJson())) - } else { - if BaseURL != "" { // 其次BaseURL - req, err = http.NewRequest(c.Request.Method, BaseURL+c.Request.RequestURI, bytes.NewReader(chatReq.ToByteJson())) - } else { // 最后是gateway的endpoint - req, err = http.NewRequest(c.Request.Method, AIGateWay_Endpoint, bytes.NewReader(chatReq.ToByteJson())) + switch onekey.ApiType { + case "azure": + var buildurl string + if onekey.EndPoint != "" { + buildurl = fmt.Sprintf("%s/openai/deployments/%s/chat/completions?api-version=%s", onekey.EndPoint, modelmap(chatReq.Model), AzureApiVersion) + } else { + buildurl = fmt.Sprintf("https://%s.openai.azure.com/openai/deployments/%s/chat/completions?api-version=%s", onekey.ResourceNmae, modelmap(chatReq.Model), AzureApiVersion) } + req, err = http.NewRequest(c.Request.Method, buildurl, bytes.NewReader(chatReq.ToByteJson())) + req.Header = c.Request.Header + req.Header.Set("api-key", onekey.Key) + default: + if onekey.EndPoint != "" { // 优先key的endpoint + req, err = http.NewRequest(c.Request.Method, onekey.EndPoint+c.Request.RequestURI, bytes.NewReader(chatReq.ToByteJson())) + } else { + if BaseURL != "" { // 其次BaseURL + req, err = http.NewRequest(c.Request.Method, BaseURL+c.Request.RequestURI, bytes.NewReader(chatReq.ToByteJson())) + } else { // 最后是gateway的endpoint + req, err = http.NewRequest(c.Request.Method, AIGateWay_Endpoint, bytes.NewReader(chatReq.ToByteJson())) + } + } + req.Header = c.Request.Header + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", onekey.Key)) } if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } - req.Header = c.Request.Header - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", onekey.Key)) - resp, err := http.DefaultClient.Do(req) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) @@ -301,6 +314,14 @@ func ChatProxy(c *gin.Context, chatReq *ChatCompletionRequest) { } } +func modelmap(in string) string { + // gpt-3.5-turbo -> gpt-35-turbo + if strings.Contains(in, ".") { + return strings.ReplaceAll(in, ".", "") + } + return in +} + type ErrResponse struct { Error struct { Message string `json:"message"` diff --git a/store/cache.go b/store/cache.go index c78ecc2..64009f4 100644 --- a/store/cache.go +++ b/store/cache.go @@ -49,6 +49,11 @@ func SelectKeyCache(apitype string) (Key, error) { if item.Object.(Key).ApiType == apitype { keys = append(keys, item.Object.(Key)) } + if apitype == "openai" { + if item.Object.(Key).ApiType == "azure" { + keys = append(keys, item.Object.(Key)) + } + } } if len(keys) == 0 { return Key{}, errors.New("No key found") From 8d6c90c550fce684f961bdee08e37c88c6ff9748 Mon Sep 17 00:00:00 2001 From: Sakurasan <26715255+Sakurasan@users.noreply.github.com> Date: Wed, 17 Apr 2024 14:41:52 +0800 Subject: [PATCH 4/7] fix: claude request --- pkg/claude/chat.go | 51 ++++++++++++++++++---------------------------- 1 file changed, 20 insertions(+), 31 deletions(-) diff --git a/pkg/claude/chat.go b/pkg/claude/chat.go index 7532fb2..64d5f01 100644 --- a/pkg/claude/chat.go +++ b/pkg/claude/chat.go @@ -26,7 +26,7 @@ func ChatTextCompletions(c *gin.Context, chatReq *openai.ChatCompletionRequest) } -type ClaudeRequest struct { +type ChatRequest struct { Model string `json:"model,omitempty"` Messages any `json:"messages,omitempty"` MaxTokens int `json:"max_tokens,omitempty"` @@ -37,34 +37,34 @@ type ClaudeRequest struct { Temperature float64 `json:"temperature,omitempty"` } -func (c *ClaudeRequest) ByteJson() []byte { +func (c *ChatRequest) ByteJson() []byte { bytejson, _ := json.Marshal(c) return bytejson } -type ClaudeMessages struct { +type ChatMessage struct { Role string `json:"role,omitempty"` Content string `json:"content,omitempty"` } type VisionMessages struct { Role string `json:"role,omitempty"` - Content []ClaudeContent `json:"content,omitempty"` + Content []VisionContent `json:"content,omitempty"` } -type ClaudeContent struct { +type VisionContent struct { Type string `json:"type,omitempty"` - Source *ClaudeSource `json:"source,omitempty"` + Source *VisionSource `json:"source,omitempty"` Text string `json:"text,omitempty"` } -type ClaudeSource struct { +type VisionSource struct { Type string `json:"type,omitempty"` MediaType string `json:"media_type,omitempty"` Data string `json:"data,omitempty"` } -type ClaudeResponse struct { +type ChatResponse struct { ID string `json:"id"` Type string `json:"type"` Role string `json:"role"` @@ -120,40 +120,33 @@ func ChatMessages(c *gin.Context, chatReq *openai.ChatCompletionRequest) { // var haveImages bool usagelog := store.Tokens{Model: chatReq.Model} - var claudReq ClaudeRequest + var claudReq ChatRequest claudReq.Model = chatReq.Model claudReq.Stream = chatReq.Stream claudReq.Temperature = chatReq.Temperature claudReq.TopP = chatReq.TopP claudReq.MaxTokens = 4096 - var msgs []any var prompt string + + var claudecontent []VisionContent for _, msg := range chatReq.Messages { if msg.Role == "system" { claudReq.System = string(msg.Content) continue } - var visioncontent []openai.VisionContent - if err := json.Unmarshal(msg.Content, &visioncontent); err != nil { + var oaivisioncontent []openai.VisionContent + if err := json.Unmarshal(msg.Content, &oaivisioncontent); err != nil { prompt += "<" + msg.Role + ">: " + string(msg.Content) + "\n" - var claudemsgs ClaudeMessages - claudemsgs.Role = msg.Role - claudemsgs.Content = string(msg.Content) - msgs = append(msgs, claudemsgs) - + claudecontent = append(claudecontent, VisionContent{Type: "text", Text: msg.Role + ":" + string(msg.Content)}) } else { - if len(visioncontent) > 0 { - var visionMessage VisionMessages - visionMessage.Role = msg.Role - - for _, content := range visioncontent { - var claudecontent []ClaudeContent + if len(oaivisioncontent) > 0 { + for _, content := range oaivisioncontent { if content.Type == "text" { prompt += "<" + msg.Role + ">: " + content.Text + "\n" - claudecontent = append(claudecontent, ClaudeContent{Type: "text", Text: content.Text}) + claudecontent = append(claudecontent, VisionContent{Type: "text", Text: msg.Role + ":" + content.Text}) } else if content.Type == "image_url" { if strings.HasPrefix(content.ImageURL.URL, "http") { fmt.Println("链接:", content.ImageURL.URL) @@ -168,22 +161,18 @@ func ChatMessages(c *gin.Context, chatReq *openai.ChatCompletionRequest) { if strings.HasPrefix(content.ImageURL.URL, "data:image/png") { mediaType = "image/png" } - claudecontent = append(claudecontent, ClaudeContent{Type: "image", Source: &ClaudeSource{Type: "base64", MediaType: mediaType, Data: strings.Split(content.ImageURL.URL, ",")[1]}}) - // haveImages = true - + claudecontent = append(claudecontent, VisionContent{Type: "image", Source: &VisionSource{Type: "base64", MediaType: mediaType, Data: strings.Split(content.ImageURL.URL, ",")[1]}}) } - visionMessage.Content = claudecontent } - msgs = append(msgs, visionMessage) + } } - claudReq.Messages = msgs - // if len(chatReq.Tools) > 0 { // tooljson, _ := json.Marshal(chatReq.Tools) // prompt += ": " + string(tooljson) + "\n" // } } + claudReq.Messages = []VisionMessages{{Role: "user", Content: claudecontent}} usagelog.PromptCount = tokenizer.NumTokensFromStr(prompt, chatReq.Model) From 4cd019e11eafd1c6a08c1627e71a15b52e45d9bb Mon Sep 17 00:00:00 2001 From: Sakurasan <26715255+Sakurasan@users.noreply.github.com> Date: Wed, 17 Apr 2024 16:00:21 +0800 Subject: [PATCH 5/7] fix: header --- pkg/claude/chat.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/pkg/claude/chat.go b/pkg/claude/chat.go index 64d5f01..e37f3c9 100644 --- a/pkg/claude/chat.go +++ b/pkg/claude/chat.go @@ -117,7 +117,12 @@ type ClaudeStreamResponse struct { } func ChatMessages(c *gin.Context, chatReq *openai.ChatCompletionRequest) { - // var haveImages bool + + onekey, err := store.SelectKeyCache("openai") + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } usagelog := store.Tokens{Model: chatReq.Model} var claudReq ChatRequest @@ -177,6 +182,10 @@ func ChatMessages(c *gin.Context, chatReq *openai.ChatCompletionRequest) { usagelog.PromptCount = tokenizer.NumTokensFromStr(prompt, chatReq.Model) req, _ := http.NewRequest("POST", MessageEndpoint, strings.NewReader(fmt.Sprintf("%v", bytes.NewReader(claudReq.ByteJson())))) + req.Header.Set("x-api-key", onekey.Key) + req.Header.Set("anthropic-version", "2023-06-01") + req.Header.Set("Content-Type", "application/json") + client := http.DefaultClient rsp, err := client.Do(req) if err != nil { From c37760c78bb137e2ce3ef2a4a3128c44b101a590 Mon Sep 17 00:00:00 2001 From: Sakurasan <26715255+Sakurasan@users.noreply.github.com> Date: Wed, 17 Apr 2024 18:04:53 +0800 Subject: [PATCH 6/7] fix: bug --- pkg/claude/chat.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/claude/chat.go b/pkg/claude/chat.go index e37f3c9..793ea0c 100644 --- a/pkg/claude/chat.go +++ b/pkg/claude/chat.go @@ -118,7 +118,7 @@ type ClaudeStreamResponse struct { func ChatMessages(c *gin.Context, chatReq *openai.ChatCompletionRequest) { - onekey, err := store.SelectKeyCache("openai") + onekey, err := store.SelectKeyCache("claude") if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return @@ -128,7 +128,7 @@ func ChatMessages(c *gin.Context, chatReq *openai.ChatCompletionRequest) { var claudReq ChatRequest claudReq.Model = chatReq.Model claudReq.Stream = chatReq.Stream - claudReq.Temperature = chatReq.Temperature + // claudReq.Temperature = chatReq.Temperature claudReq.TopP = chatReq.TopP claudReq.MaxTokens = 4096 @@ -181,7 +181,7 @@ func ChatMessages(c *gin.Context, chatReq *openai.ChatCompletionRequest) { usagelog.PromptCount = tokenizer.NumTokensFromStr(prompt, chatReq.Model) - req, _ := http.NewRequest("POST", MessageEndpoint, strings.NewReader(fmt.Sprintf("%v", bytes.NewReader(claudReq.ByteJson())))) + req, _ := http.NewRequest("POST", MessageEndpoint, bytes.NewReader(claudReq.ByteJson())) req.Header.Set("x-api-key", onekey.Key) req.Header.Set("anthropic-version", "2023-06-01") req.Header.Set("Content-Type", "application/json") @@ -200,7 +200,7 @@ func ChatMessages(c *gin.Context, chatReq *openai.ChatCompletionRequest) { teeReader := io.TeeReader(rsp.Body, c.Writer) - dataChan := make(chan string, 1) + dataChan := make(chan string) // stopChan := make(chan bool) var result string From 3b609324b86937026e055552138c560c03a83aee Mon Sep 17 00:00:00 2001 From: Sakurasan <26715255+Sakurasan@users.noreply.github.com> Date: Wed, 17 Apr 2024 19:29:52 +0800 Subject: [PATCH 7/7] fix bug --- pkg/claude/chat.go | 8 ++------ pkg/tokenizer/tokenizer.go | 41 +++++++++++++++++++++++++++++++++----- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/pkg/claude/chat.go b/pkg/claude/chat.go index 793ea0c..8987dc7 100644 --- a/pkg/claude/chat.go +++ b/pkg/claude/chat.go @@ -197,8 +197,8 @@ func ChatMessages(c *gin.Context, chatReq *openai.ChatCompletionRequest) { io.Copy(c.Writer, rsp.Body) return } - - teeReader := io.TeeReader(rsp.Body, c.Writer) + var buffer bytes.Buffer + teeReader := io.TeeReader(rsp.Body, &buffer) dataChan := make(chan string) // stopChan := make(chan bool) @@ -246,10 +246,6 @@ func ChatMessages(c *gin.Context, chatReq *openai.ChatCompletionRequest) { if claudeResp.Delta.StopReason != "" { dataChan <- "\ndata: [DONE]\n" } - } else { - if !bytes.HasPrefix(line, []byte("event:")) { - dataChan <- string(line) + "\n" - } } } defer close(dataChan) diff --git a/pkg/tokenizer/tokenizer.go b/pkg/tokenizer/tokenizer.go index ac67bae..b1a6987 100644 --- a/pkg/tokenizer/tokenizer.go +++ b/pkg/tokenizer/tokenizer.go @@ -83,17 +83,17 @@ func Cost(model string, promptCount, completionCount int) float64 { switch model { case "gpt-3.5-turbo-0301": cost = 0.002 * float64((prompt+completion)/1000) - case "gpt-3.5-turbo", "gpt-3.5-turbo-0613", "gpt-3.5-turbo-1106": + case "gpt-3.5-turbo", "gpt-3.5-turbo-0613", "gpt-3.5-turbo-1106", "gpt-3.5-turbo-0125": cost = 0.0015*float64((prompt)/1000) + 0.002*float64(completion/1000) case "gpt-3.5-turbo-16k", "gpt-3.5-turbo-16k-0613": cost = 0.003*float64((prompt)/1000) + 0.004*float64(completion/1000) - case "gpt-3.5-turbo-instruct", "gpt-3.5-turbo-instruct-0914": - cost = 0.0015*float64((prompt)/1000) + 0.002*float64(completion/1000) case "gpt-4", "gpt-4-0613", "gpt-4-0314": cost = 0.03*float64(prompt/1000) + 0.06*float64(completion/1000) case "gpt-4-32k", "gpt-4-32k-0314", "gpt-4-32k-0613": cost = 0.06*float64(prompt/1000) + 0.12*float64(completion/1000) - case "gpt-4-1106-preview", "gpt-4-vision-preview": + case "gpt-4-1106-preview", "gpt-4-vision-preview", "gpt-4-0125-preview", "gpt-4-turbo-preview": + cost = 0.01*float64(prompt/1000) + 0.03*float64(completion/1000) + case "gpt-4-turbo", "gpt-4-turbo-2024-04-09": cost = 0.01*float64(prompt/1000) + 0.03*float64(completion/1000) case "whisper-1": // 0.006$/min @@ -126,12 +126,43 @@ func Cost(model string, promptCount, completionCount int) float64 { cost = float64(0.12 * completion) // claude /million tokens + // https://aws.amazon.com/cn/bedrock/pricing/ case "claude-v1", "claude-v1-100k": cost = 11.02/1000000*float64(prompt) + (32.68/1000000)*float64(completion) case "claude-instant-v1", "claude-instant-v1-100k": cost = (1.63/1000000)*float64(prompt) + (5.51/1000000)*float64(completion) case "claude-2", "claude-2.1": - cost = (11.02/1000000)*float64(prompt) + (32.68/1000000)*float64(completion) + cost = (8.0/1000000)*float64(prompt) + (24.0/1000000)*float64(completion) + case "claude-3-haiku": + cost = (0.00025/1000)*float64(prompt) + (0.00125/1000)*float64(completion) + case "claude-3-sonnet": + cost = (0.003/1000)*float64(prompt) + (0.015/1000)*float64(completion) + case "claude-3-opus": + cost = (0.015/1000)*float64(prompt) + (0.075/1000)*float64(completion) + case "claude-3-haiku-20240307": + cost = (0.00025/1000)*float64(prompt) + (0.00125/1000)*float64(completion) + case "claude-3-sonnet-20240229": + cost = (0.003/1000)*float64(prompt) + (0.015/1000)*float64(completion) + case "claude-3-opus-20240229": + cost = (0.015/1000)*float64(prompt) + (0.075/1000)*float64(completion) + + // google + // https://ai.google.dev/pricing?hl=zh-cn + case "gemini-pro": + cost = (0.000125/1000)*float64(prompt) + (0.000375/1000)*float64(completion) + case "gemini-pro-vision": + cost = (0.000125/1000)*float64(prompt) + (0.000375/1000)*float64(completion) + case "gemini-1.5-pro-latest": + cost = (0.00025/1000)*float64(prompt) + (0.0005/1000)*float64(completion) + + // Mistral AI + case "mistral-small-latest": + cost = (0.002/1000)*float64(prompt) + (0.006/1000)*float64(completion) + case "mistral-medium-latest": + cost = (0.0027/1000)*float64(prompt) + (0.0081/1000)*float64(completion) + case "mistral-large-latest": + cost = (0.008/1000)*float64(prompt) + (0.024/1000)*float64(completion) + default: if strings.Contains(model, "gpt-3.5-turbo") { cost = 0.003 * float64((prompt+completion)/1000)