From 2ff19d8548cd536c9bbb5b48a6480c718ead10ff Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 20 Mar 2026 05:18:09 +0000 Subject: [PATCH 1/2] feat: replace custom LLM providers with langchaingo Replace ~275 lines of manual HTTP client code in azure.go and ollama.go with langchaingo v0.1.14 wrappers. Also replace custom chunker with langchaingo's textsplitter.RecursiveCharacter. https://claude.ai/code/session_012AA3yhoX3envjc7dZsQQra --- go.mod | 15 +++- go.sum | 52 +++++++++---- internal/chunker/chunker.go | 108 ++++---------------------- internal/llm/azure.go | 147 ------------------------------------ internal/llm/ollama.go | 126 ------------------------------- internal/llm/provider.go | 79 ++++++++++++++++++- 6 files changed, 139 insertions(+), 388 deletions(-) delete mode 100644 internal/llm/azure.go delete mode 100644 internal/llm/ollama.go diff --git a/go.mod b/go.mod index cdad5b1..23b9be3 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/schollz/progressbar/v3 v3.17.1 github.com/spf13/cobra v1.8.1 github.com/spf13/viper v1.19.0 + github.com/tmc/langchaingo v0.1.14 golang.org/x/net v0.52.0 modernc.org/sqlite v1.29.10 ) @@ -16,6 +17,7 @@ require ( require ( github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/buger/jsonparser v1.1.1 // indirect + github.com/dlclark/regexp2 v1.10.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect @@ -25,7 +27,7 @@ require ( github.com/hhrutter/tiff v1.0.2 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/invopop/jsonschema v0.13.0 // indirect - github.com/magiconair/properties v1.8.7 // indirect + github.com/magiconair/properties v1.8.10 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect @@ -34,6 +36,7 @@ require ( github.com/ncruces/go-strftime v0.1.9 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/pkoukk/tiktoken-go v0.1.6 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect @@ -45,10 +48,14 @@ require ( github.com/subosito/gotenv v1.6.0 // indirect github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect github.com/yosida95/uritemplate/v3 v3.0.2 // indirect - go.uber.org/atomic v1.9.0 // indirect - go.uber.org/multierr v1.9.0 // indirect + gitlab.com/golang-commonmark/html v0.0.0-20191124015941-a22733972181 // indirect + gitlab.com/golang-commonmark/linkify v0.0.0-20191026162114-a0c2df6c8f82 // indirect + gitlab.com/golang-commonmark/markdown v0.0.0-20211110145824-bf3e522c626a // indirect + gitlab.com/golang-commonmark/mdurl v0.0.0-20191124015652-932350d1cb84 // indirect + gitlab.com/golang-commonmark/puny v0.0.0-20191124015043-9f83538fa04f // indirect + go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.49.0 // indirect - golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect + golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa // indirect golang.org/x/image v0.26.0 // indirect golang.org/x/sys v0.42.0 // indirect golang.org/x/term v0.41.0 // indirect diff --git a/go.sum b/go.sum index 43c8a8b..dbfced2 100644 --- a/go.sum +++ b/go.sum @@ -9,14 +9,16 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs 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/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0= +github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= 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/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +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/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo= github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -40,8 +42,8 @@ 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/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= -github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= +github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mark3labs/mcp-go v0.45.0 h1:s0S8qR/9fWaQ3pHxz7pm1uQ0DrswoSnRIxKIjbiQtkc= @@ -56,12 +58,14 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= -github.com/pdfcpu/pdfcpu v0.10.0 h1:K7Hv2tW/poMvG+DuowVGfYvNcN1Y7USS+8IebA3Z8+w= +github.com/pdfcpu/pdfcpu v0.10.0 h1:UXqRZ93iV9+lK7kwfa7N+EeZdUpB6ct8F0Ev8EXWhfk= github.com/pdfcpu/pdfcpu v0.10.0/go.mod h1:Q2Z3sqdRqHTdIq1mPAUl8nfAoim8p3c1ASOaQ10mCpE= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkoukk/tiktoken-go v0.1.6 h1:JF0TlJzhTbrI30wCvFuiw6FzP2+/bR+FIxUdgEAcUsw= +github.com/pkoukk/tiktoken-go v0.1.6/go.mod h1:9NiV+i9mJKGj1rYOT+njbv+ZwA/zJxYdewGl6qVatpg= github.com/pmezard/go-difflib v1.0.0/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= @@ -70,8 +74,9 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qq github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= @@ -94,27 +99,40 @@ github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+ 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 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 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.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/tmc/langchaingo v0.1.14 h1:o1qWBPigAIuFvrG6cjTFo0cZPFEZ47ZqpOYMjM15yZc= +github.com/tmc/langchaingo v0.1.14/go.mod h1:aKKYXYoqhIDEv7WKdpnnCLRaqXic69cX9MnDUk72378= 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/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.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= -go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +gitlab.com/golang-commonmark/html v0.0.0-20191124015941-a22733972181 h1:K+bMSIx9A7mLES1rtG+qKduLIXq40DAzYHtb0XuCukA= +gitlab.com/golang-commonmark/html v0.0.0-20191124015941-a22733972181/go.mod h1:dzYhVIwWCtzPAa4QP98wfB9+mzt33MSmM8wsKiMi2ow= +gitlab.com/golang-commonmark/linkify v0.0.0-20191026162114-a0c2df6c8f82 h1:oYrL81N608MLZhma3ruL8qTM4xcpYECGut8KSxRY59g= +gitlab.com/golang-commonmark/linkify v0.0.0-20191026162114-a0c2df6c8f82/go.mod h1:Gn+LZmCrhPECMD3SOKlE+BOHwhOYD9j7WT9NUtkCrC8= +gitlab.com/golang-commonmark/markdown v0.0.0-20211110145824-bf3e522c626a h1:O85GKETcmnCNAfv4Aym9tepU8OE0NmcZNqPlXcsBKBs= +gitlab.com/golang-commonmark/markdown v0.0.0-20211110145824-bf3e522c626a/go.mod h1:LaSIs30YPGs1H5jwGgPhLzc8vkNc/k0rDX/fEZqiU/M= +gitlab.com/golang-commonmark/mdurl v0.0.0-20191124015652-932350d1cb84 h1:qqjvoVXdWIcZCLPMlzgA7P9FZWdPGPvP/l3ef8GzV6o= +gitlab.com/golang-commonmark/mdurl v0.0.0-20191124015652-932350d1cb84/go.mod h1:IJZ+fdMvbW2qW6htJx7sLJ04FEs4Ldl/MDsJtMKywfw= +gitlab.com/golang-commonmark/puny v0.0.0-20191124015043-9f83538fa04f h1:Wku8eEdeJqIOFHtrfkYUByc4bCaTeA6fL0UJgfEiFMI= +gitlab.com/golang-commonmark/puny v0.0.0-20191124015043-9f83538fa04f/go.mod h1:Tiuhl+njh/JIg0uS/sOJVYi0x2HEa5rc1OAaVsb5tAs= +gitlab.com/opennota/wd v0.0.0-20180912061657-c5d65f63c638 h1:uPZaMiz6Sz0PZs3IZJWpU5qHKGNy///1pacZC9txiUI= +gitlab.com/opennota/wd v0.0.0-20180912061657-c5d65f63c638/go.mod h1:EGRJaqe2eO9XGmFtQCvV3Lm9NLico3UhFwUpCG/+mVU= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 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/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI= +golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= golang.org/x/image v0.26.0 h1:4XjIFEZWQmCZi6Wv8BoxsDhRU3RVnLX04dToTDAEPlY= golang.org/x/image v0.26.0/go.mod h1:lcxbMFAovzpnJxzXS3nyL83K27tmqtKzIJpctK8YO5c= golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= @@ -128,8 +146,10 @@ 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/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -168,3 +188,5 @@ modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/internal/chunker/chunker.go b/internal/chunker/chunker.go index 614177e..1ff4ab4 100644 --- a/internal/chunker/chunker.go +++ b/internal/chunker/chunker.go @@ -1,123 +1,43 @@ package chunker import ( - "strings" "unicode/utf8" + + "github.com/tmc/langchaingo/textsplitter" ) -// Chunk is a piece of text from a document. type Chunk struct { Index int Content string Tokens int } -// Chunker splits text into overlapping chunks. type Chunker struct { - ChunkSize int - ChunkOverlap int - separators []string + splitter textsplitter.RecursiveCharacter } -// New creates a recursive character text splitter. func New(chunkSize, chunkOverlap int) *Chunker { return &Chunker{ - ChunkSize: chunkSize, - ChunkOverlap: chunkOverlap, - separators: []string{"\n\n", "\n", ". ", " ", ""}, + splitter: textsplitter.NewRecursiveCharacter( + textsplitter.WithChunkSize(chunkSize), + textsplitter.WithChunkOverlap(chunkOverlap), + textsplitter.WithSeparators([]string{"\n\n", "\n", ". ", " ", ""}), + ), } } -// Split splits text into chunks, returning them with indices. func (c *Chunker) Split(text string) []Chunk { - pieces := c.splitText(text, c.separators) - return c.mergePieces(pieces) -} - -func (c *Chunker) splitText(text string, separators []string) []string { - if len(separators) == 0 || utf8.RuneCountInString(text) <= c.ChunkSize { - return []string{text} - } - sep := separators[0] - rest := separators[1:] - - if sep == "" { - // Split by rune - runes := []rune(text) - var parts []string - for i := 0; i < len(runes); i += c.ChunkSize { - end := i + c.ChunkSize - if end > len(runes) { - end = len(runes) - } - parts = append(parts, string(runes[i:end])) - } - return parts - } - - splits := strings.Split(text, sep) - var good, bad []string - for _, s := range splits { - if s == "" { - continue - } - if utf8.RuneCountInString(s) <= c.ChunkSize { - good = append(good, s) - } else { - // Recursively split the large piece - sub := c.splitText(s, rest) - // Flush good before adding sub - bad = append(bad, good...) - good = nil - bad = append(bad, sub...) - } + parts, err := c.splitter.SplitText(text) + if err != nil { + return []Chunk{{Index: 0, Content: text, Tokens: estimateTokens(text)}} } - return append(bad, good...) -} - -func (c *Chunker) mergePieces(pieces []string) []Chunk { - var chunks []Chunk - var current strings.Builder - idx := 0 - - flush := func() { - text := strings.TrimSpace(current.String()) - if text != "" { - chunks = append(chunks, Chunk{ - Index: idx, - Content: text, - Tokens: estimateTokens(text), - }) - idx++ - } - } - - for _, piece := range pieces { - pieceLen := utf8.RuneCountInString(piece) - curLen := utf8.RuneCountInString(current.String()) - - if curLen+pieceLen > c.ChunkSize && curLen > 0 { - flush() - // Keep overlap - curText := current.String() - curRunes := []rune(curText) - overlapStart := len(curRunes) - c.ChunkOverlap - if overlapStart < 0 { - overlapStart = 0 - } - current.Reset() - current.WriteString(string(curRunes[overlapStart:])) - } - if current.Len() > 0 { - current.WriteString(" ") - } - current.WriteString(piece) + chunks := make([]Chunk, len(parts)) + for i, p := range parts { + chunks[i] = Chunk{Index: i, Content: p, Tokens: estimateTokens(p)} } - flush() return chunks } -// estimateTokens approximates token count (1 token ≈ 4 chars). func estimateTokens(text string) int { return utf8.RuneCountInString(text) / 4 } diff --git a/internal/llm/azure.go b/internal/llm/azure.go deleted file mode 100644 index 84c1d14..0000000 --- a/internal/llm/azure.go +++ /dev/null @@ -1,147 +0,0 @@ -package llm - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "io" - "net/http" - - "github.com/RandomCodeSpace/docscontext/internal/config" -) - -type azureProvider struct { - endpoint string - apiKey string - apiVersion string - chatModel string - embedModel string - client *http.Client -} - -func newAzureProvider(cfg *config.LLMConfig) (Provider, error) { - return &azureProvider{ - endpoint: cfg.Azure.Endpoint, - apiKey: cfg.Azure.APIKey, - apiVersion: cfg.Azure.APIVersion, - chatModel: cfg.Azure.ChatModel, - embedModel: cfg.Azure.EmbedModel, - client: &http.Client{}, - }, nil -} - -func (p *azureProvider) Name() string { return "azure" } -func (p *azureProvider) ModelID() string { return p.chatModel } - -func (p *azureProvider) Complete(ctx context.Context, prompt string, opts ...Option) (string, error) { - o := applyOptions(opts) - - type message struct { - Role string `json:"role"` - Content string `json:"content"` - } - reqBody := map[string]any{ - "messages": []message{{Role: "user", Content: prompt}}, - "max_tokens": o.maxTokens, - "temperature": o.temperature, - } - if o.jsonMode { - reqBody["response_format"] = map[string]string{"type": "json_object"} - } - - body, err := json.Marshal(reqBody) - if err != nil { - return "", fmt.Errorf("azure complete marshal: %w", err) - } - - url := fmt.Sprintf("%s/openai/deployments/%s/chat/completions?api-version=%s", - p.endpoint, p.chatModel, p.apiVersion) - req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(body)) - if err != nil { - return "", fmt.Errorf("azure complete request: %w", err) - } - req.Header.Set("Content-Type", "application/json") - req.Header.Set("api-key", p.apiKey) - - resp, err := p.client.Do(req) - if err != nil { - return "", fmt.Errorf("azure complete: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - b, _ := io.ReadAll(resp.Body) - return "", fmt.Errorf("azure complete HTTP %d: %s", resp.StatusCode, b) - } - - var result struct { - Choices []struct { - Message struct { - Content string `json:"content"` - } `json:"message"` - } `json:"choices"` - } - if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { - return "", fmt.Errorf("azure complete decode: %w", err) - } - if len(result.Choices) == 0 { - return "", fmt.Errorf("azure complete: empty response") - } - return result.Choices[0].Message.Content, nil -} - -func (p *azureProvider) Embed(ctx context.Context, text string) ([]float32, error) { - vecs, err := p.EmbedBatch(ctx, []string{text}) - if err != nil { - return nil, err - } - if len(vecs) == 0 { - return nil, fmt.Errorf("azure embed: empty response") - } - return vecs[0], nil -} - -func (p *azureProvider) EmbedBatch(ctx context.Context, texts []string) ([][]float32, error) { - reqBody := map[string]any{"input": texts} - body, err := json.Marshal(reqBody) - if err != nil { - return nil, fmt.Errorf("azure embed marshal: %w", err) - } - - url := fmt.Sprintf("%s/openai/deployments/%s/embeddings?api-version=%s", - p.endpoint, p.embedModel, p.apiVersion) - req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(body)) - if err != nil { - return nil, fmt.Errorf("azure embed request: %w", err) - } - req.Header.Set("Content-Type", "application/json") - req.Header.Set("api-key", p.apiKey) - - resp, err := p.client.Do(req) - if err != nil { - return nil, fmt.Errorf("azure embed: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - b, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("azure embed HTTP %d: %s", resp.StatusCode, b) - } - - var result struct { - Data []struct { - Embedding []float32 `json:"embedding"` - } `json:"data"` - } - if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { - return nil, fmt.Errorf("azure embed decode: %w", err) - } - - vecs := make([][]float32, len(result.Data)) - for i, d := range result.Data { - vecs[i] = d.Embedding - } - return vecs, nil -} - diff --git a/internal/llm/ollama.go b/internal/llm/ollama.go deleted file mode 100644 index 0451156..0000000 --- a/internal/llm/ollama.go +++ /dev/null @@ -1,126 +0,0 @@ -package llm - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "io" - "net/http" - - "github.com/RandomCodeSpace/docscontext/internal/config" -) - -type ollamaProvider struct { - baseURL string - chatModel string - embedModel string - client *http.Client -} - -func newOllamaProvider(cfg *config.LLMConfig) (Provider, error) { - return &ollamaProvider{ - baseURL: cfg.Ollama.BaseURL, - chatModel: cfg.Ollama.ChatModel, - embedModel: cfg.Ollama.EmbedModel, - client: &http.Client{}, - }, nil -} - -func (p *ollamaProvider) Name() string { return "ollama" } -func (p *ollamaProvider) ModelID() string { return p.chatModel } - -func (p *ollamaProvider) Complete(ctx context.Context, prompt string, opts ...Option) (string, error) { - o := applyOptions(opts) - - reqBody := map[string]any{ - "model": p.chatModel, - "prompt": prompt, - "stream": false, - "options": map[string]any{ - "num_predict": o.maxTokens, - "temperature": o.temperature, - }, - } - if o.jsonMode { - reqBody["format"] = "json" - } - - body, err := json.Marshal(reqBody) - if err != nil { - return "", fmt.Errorf("ollama complete marshal: %w", err) - } - - req, err := http.NewRequestWithContext(ctx, http.MethodPost, p.baseURL+"/api/generate", bytes.NewReader(body)) - if err != nil { - return "", fmt.Errorf("ollama complete request: %w", err) - } - req.Header.Set("Content-Type", "application/json") - - resp, err := p.client.Do(req) - if err != nil { - return "", fmt.Errorf("ollama complete: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - b, _ := io.ReadAll(resp.Body) - return "", fmt.Errorf("ollama complete HTTP %d: %s", resp.StatusCode, b) - } - - var result struct { - Response string `json:"response"` - } - if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { - return "", fmt.Errorf("ollama complete decode: %w", err) - } - return result.Response, nil -} - -func (p *ollamaProvider) Embed(ctx context.Context, text string) ([]float32, error) { - vecs, err := p.EmbedBatch(ctx, []string{text}) - if err != nil { - return nil, err - } - if len(vecs) == 0 { - return nil, fmt.Errorf("ollama embed: empty response") - } - return vecs[0], nil -} - -func (p *ollamaProvider) EmbedBatch(ctx context.Context, texts []string) ([][]float32, error) { - reqBody := map[string]any{ - "model": p.embedModel, - "input": texts, - } - body, err := json.Marshal(reqBody) - if err != nil { - return nil, fmt.Errorf("ollama embed marshal: %w", err) - } - - req, err := http.NewRequestWithContext(ctx, http.MethodPost, p.baseURL+"/api/embed", bytes.NewReader(body)) - if err != nil { - return nil, fmt.Errorf("ollama embed request: %w", err) - } - req.Header.Set("Content-Type", "application/json") - - resp, err := p.client.Do(req) - if err != nil { - return nil, fmt.Errorf("ollama embed: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - b, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("ollama embed HTTP %d: %s", resp.StatusCode, b) - } - - var result struct { - Embeddings [][]float32 `json:"embeddings"` - } - if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { - return nil, fmt.Errorf("ollama embed decode: %w", err) - } - return result.Embeddings, nil -} - diff --git a/internal/llm/provider.go b/internal/llm/provider.go index 3558942..6129604 100644 --- a/internal/llm/provider.go +++ b/internal/llm/provider.go @@ -5,6 +5,10 @@ import ( "fmt" "github.com/RandomCodeSpace/docscontext/internal/config" + "github.com/tmc/langchaingo/embeddings" + "github.com/tmc/langchaingo/llms" + "github.com/tmc/langchaingo/llms/ollama" + "github.com/tmc/langchaingo/llms/openai" ) // Option configures LLM completion. @@ -16,9 +20,9 @@ type callOptions struct { jsonMode bool } -func WithMaxTokens(n int) Option { return func(o *callOptions) { o.maxTokens = n } } +func WithMaxTokens(n int) Option { return func(o *callOptions) { o.maxTokens = n } } func WithTemperature(t float64) Option { return func(o *callOptions) { o.temperature = t } } -func WithJSONMode() Option { return func(o *callOptions) { o.jsonMode = true } } +func WithJSONMode() Option { return func(o *callOptions) { o.jsonMode = true } } func applyOptions(opts []Option) *callOptions { o := &callOptions{maxTokens: 2048, temperature: 0.0} @@ -49,3 +53,74 @@ func NewProvider(cfg *config.LLMConfig) (Provider, error) { } } +// lcProvider adapts langchaingo to our Provider interface. +type lcProvider struct { + llm llms.Model + emb embeddings.Embedder + name string + modelID string +} + +func (p *lcProvider) Name() string { return p.name } +func (p *lcProvider) ModelID() string { return p.modelID } + +func (p *lcProvider) Complete(ctx context.Context, prompt string, opts ...Option) (string, error) { + o := applyOptions(opts) + callOpts := []llms.CallOption{ + llms.WithMaxTokens(o.maxTokens), + llms.WithTemperature(o.temperature), + } + if o.jsonMode { + callOpts = append(callOpts, llms.WithJSONMode()) + } + return llms.GenerateFromSinglePrompt(ctx, p.llm, prompt, callOpts...) +} + +func (p *lcProvider) Embed(ctx context.Context, text string) ([]float32, error) { + return p.emb.EmbedQuery(ctx, text) +} + +func (p *lcProvider) EmbedBatch(ctx context.Context, texts []string) ([][]float32, error) { + return p.emb.EmbedDocuments(ctx, texts) +} + +func newOllamaProvider(cfg *config.LLMConfig) (Provider, error) { + chatLLM, err := ollama.New( + ollama.WithServerURL(cfg.Ollama.BaseURL), + ollama.WithModel(cfg.Ollama.ChatModel), + ) + if err != nil { + return nil, fmt.Errorf("ollama chat LLM: %w", err) + } + embedLLM, err := ollama.New( + ollama.WithServerURL(cfg.Ollama.BaseURL), + ollama.WithModel(cfg.Ollama.EmbedModel), + ) + if err != nil { + return nil, fmt.Errorf("ollama embed LLM: %w", err) + } + emb, err := embeddings.NewEmbedder(embedLLM) + if err != nil { + return nil, fmt.Errorf("ollama embedder: %w", err) + } + return &lcProvider{llm: chatLLM, emb: emb, name: "ollama", modelID: cfg.Ollama.EmbedModel}, nil +} + +func newAzureProvider(cfg *config.LLMConfig) (Provider, error) { + chatLLM, err := openai.New( + openai.WithBaseURL(cfg.Azure.Endpoint), + openai.WithToken(cfg.Azure.APIKey), + openai.WithAPIVersion(cfg.Azure.APIVersion), + openai.WithAPIType(openai.APITypeAzure), + openai.WithModel(cfg.Azure.ChatModel), + openai.WithEmbeddingModel(cfg.Azure.EmbedModel), + ) + if err != nil { + return nil, fmt.Errorf("azure openai LLM: %w", err) + } + emb, err := embeddings.NewEmbedder(chatLLM) + if err != nil { + return nil, fmt.Errorf("azure openai embedder: %w", err) + } + return &lcProvider{llm: chatLLM, emb: emb, name: "azure", modelID: cfg.Azure.EmbedModel}, nil +} From 720cbb415b68de9753c6446c87b92110eb9c03de Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 20 Mar 2026 05:59:26 +0000 Subject: [PATCH 2/2] fix: downgrade Go toolchain requirement from 1.25.0 to 1.24.7 The go.mod required Go 1.25.0 which isn't widely available yet. Downgrade to 1.24.7 and adjust golang.org/x/* dependencies accordingly. https://claude.ai/code/session_012AA3yhoX3envjc7dZsQQra --- go.mod | 12 ++++++------ go.sum | 32 ++++++++++++++++---------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/go.mod b/go.mod index 23b9be3..ebf4aad 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/RandomCodeSpace/docscontext -go 1.25.0 +go 1.24.7 require ( github.com/google/uuid v1.6.0 @@ -10,7 +10,7 @@ require ( github.com/spf13/cobra v1.8.1 github.com/spf13/viper v1.19.0 github.com/tmc/langchaingo v0.1.14 - golang.org/x/net v0.52.0 + golang.org/x/net v0.43.0 modernc.org/sqlite v1.29.10 ) @@ -54,12 +54,12 @@ require ( gitlab.com/golang-commonmark/mdurl v0.0.0-20191124015652-932350d1cb84 // indirect gitlab.com/golang-commonmark/puny v0.0.0-20191124015043-9f83538fa04f // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.49.0 // indirect + golang.org/x/crypto v0.41.0 // indirect golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa // indirect golang.org/x/image v0.26.0 // indirect - golang.org/x/sys v0.42.0 // indirect - golang.org/x/term v0.41.0 // indirect - golang.org/x/text v0.35.0 // indirect + golang.org/x/sys v0.35.0 // indirect + golang.org/x/term v0.34.0 // indirect + golang.org/x/text v0.28.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index dbfced2..768d276 100644 --- a/go.sum +++ b/go.sum @@ -129,29 +129,29 @@ gitlab.com/opennota/wd v0.0.0-20180912061657-c5d65f63c638 h1:uPZaMiz6Sz0PZs3IZJW gitlab.com/opennota/wd v0.0.0-20180912061657-c5d65f63c638/go.mod h1:EGRJaqe2eO9XGmFtQCvV3Lm9NLico3UhFwUpCG/+mVU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -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/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= +golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI= golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= golang.org/x/image v0.26.0 h1:4XjIFEZWQmCZi6Wv8BoxsDhRU3RVnLX04dToTDAEPlY= golang.org/x/image v0.26.0/go.mod h1:lcxbMFAovzpnJxzXS3nyL83K27tmqtKzIJpctK8YO5c= -golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= -golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= -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/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= -golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg= +golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= +golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.6.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/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= -golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= +golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= -golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= -golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= +golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= +golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=