diff --git a/Makefile b/Makefile index e0d32fc74..e04ef9fdb 100644 --- a/Makefile +++ b/Makefile @@ -233,3 +233,8 @@ catalog-build: opm ## Build a catalog image. .PHONY: catalog-push catalog-push: ## Push a catalog image. $(MAKE) docker-push IMG=$(CATALOG_IMG) + +# CLI Arcadia +.PHONY: arctl +arctl: fmt vet ## Build manager binary. + go build -o bin/arctl arctl/*.go \ No newline at end of file diff --git a/README.md b/README.md index 6029975da..b2645a773 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,16 @@ Output: {"code":200,"data":{"choices":[{"content":"\" Kubernetes (also known as K8s) is an open-source container orchestration system for automating the deployment, scaling, and management of containerized applications. It was originally designed by Google, and is now maintained by the Cloud Native Computing Foundation (CNCF).\\n\\nKubernetes provides a platform-as-a-service (PaaS) model, which allows developers to deploy, run, and scale containerized applications with minimal configuration and effort. It does this by abstracting the underlying infrastructure and providing a common set of APIs and tools that can be used to deploy, manage, and scale applications consistently across different environments.\\n\\nKubernetes is widely adopted by organizations of all sizes and has a large, active community of developers contributing to its continued development and improvement. It is available on a variety of platforms, including Linux, Windows, and 移动设备,and can be deployed on-premises, in the cloud, or in a hybrid environment.\"","role":"assistant"}],"request_id":"7865480399259975113","task_id":"7865480399259975113","task_status":"SUCCESS","usage":{"total_tokens":203}},"msg":"操作成功","success":true} ``` +## CLI + +We provide a Command Line Tool `arctl` to interact with `arcadia` and `LLMs`. See [here](./arctl/README.md) for more details. + +### Quick install + +```shell +go install github.com/kubeagi/arcadia/arctl@latest +``` + ## Packages To enhace the AI capability in Golang,we developed some packages. diff --git a/arctl/README.md b/arctl/README.md new file mode 100644 index 000000000..b5a8d778a --- /dev/null +++ b/arctl/README.md @@ -0,0 +1,172 @@ +# arctl + +arctl(arcadia command line tool) + +```shell +``` + +## Quick Install + +```shell +cd arcadia +make arctl +``` +If build succeeded, `arctl` will be built into `bin/arctl` under `arcadia` + +```shell +❯ ./bin/arctl -h +Command line tools for Arcadia + +Usage: + arctl [usage] [flags] + arctl [command] + +Available Commands: + load Load documents into VectorStore + chat Do LLM chat with similarity search(optional) + completion Generate the autocompletion script for the specified shell + help Help about any command + +Flags: + -h, --help help for arctl + +Use "arctl [command] --help" for more information about a command. +``` + +## Usage +### Load documents into vector store + +```shell +❯ ./bin/arctl load -h +Load documents into VectorStore + +Usage: + arctl load [usage] [flags] + +Flags: + --chunk-overlap int chunk overlap for embedding (default 30) + --chunk-size int chunk size for embedding (default 300) + --document string path of the document to load + --document-language string language of the document(Only text,html,csv supported now) (default "text") + --embedding-llm-apikey string apiKey to access embedding service + --embedding-llm-type string llm type to use(Only zhipuai,openai supported now) (default "zhipuai") + -h, --help help for load + --namespace string namespace/collection of the document to load into (default "arcadia") + --vector-store string vector stores to use(Only chroma supported now) (default "http://localhost:8000") +``` + +Required Arguments: +- `--embedding-llm-apikey` +- `--document` + +For example: +```shell +./bin/arctl load --embedding-llm-apikey 26b2bc55fae40752055cadfc4792f9de.wagA4NIwg5aZJWhm --document ./README.md +``` + + +### Chat with LLM +```shell +❯ ./bin/arctl chat -h +Do LLM chat with similarity search(optional) + +Usage: + arctl chat [usage] [flags] + +Flags: + --chat-llm-apikey string apiKey to access embedding service + --chat-llm-type string llm type to use(Only zhipuai,openai supported now) (default "zhipuai") + --embedding-llm-apikey string apiKey to access embedding service.Must required when embedding similarity search is enabled + --embedding-llm-type string llm type to use(Only zhipuai,openai supported now) (default "zhipuai") + --enable-embedding-search enable embedding similarity search + -h, --help help for chat + --method string Invoke method used when access LLM service(invoke/sse-invoke) (default "sse-invoke") + --model string which model to use: chatglm_lite/chatglm_std/chatglm_pro (default "chatglm_lite") + --namespace string namespace/collection to query from (default "arcadia") + --num-docs int number of documents to be returned with SimilarSearch (default 3) + --question string question text to be asked + --score-threshold float score threshold for similarity search(Higher is better) + --temperature float32 temperature for chat (default 0.95) + --top-p float32 top-p for chat (default 0.7) + --vector-store string vector stores to use(Only chroma supported now) (default "http://localhost:8000") +``` + +Now `arctl chat` has two modes which is controlled by flag `--enable-embedding-search`: +- normal chat without embedding search(default) +- enable similarity search with embedding + +#### Normal chat(Without embedding) + +```shell +./bin/arctl chat --chat-llm-apikey 26b2bc55fae40752055cadfc4792f9de.wagA4NIwg5aZJWhm --model chatglm_pro --question "介绍一下Arcadia" +``` + +Required Arguments: +- `--chat-llm-apikey` +- `--question` + + +**Output:** +```shell +Prompts: [{user 介绍一下Arcadia}] + Arcadia(阿卡迪亚)是一个知名的游戏开发团队,成立于 2007 年,总部位于美国加州洛杉矶。Arcadia 主要致力于创作高质量的社交游戏和虚拟世界,旨在为玩家提供一个沉浸式的游戏体验。他们的作品在全球范围内拥有大量的玩家,其中包括许多虚拟世界和模拟经营类的游戏。 + +Arcadia 的创始人兼 CEO 是 Raph Koster,他是一位在游戏行业拥有丰富经验的游戏设计师。在成立 Arcadia 之前,他曾担任过多家知名游戏公司的要职,如索尼在线娱乐、艺电等。 + +Arcadia 开发的游戏中最知名的作品之一是《Second Life》(第二人生),这是一款非常受欢迎的虚拟世界游戏,玩家可以在游戏中创建自己的虚拟角色、探索环境、与其他玩家互动等。这款游戏自推出以来获得了许多奖项,被誉为虚拟世界游戏的典范。 + +除了《Second Life》之外,Arcadia 还开发了许多其他游戏,如《Champions Online》、《Star Wars: Galaxies》等。此外,他们还为其他游戏公司提供游戏开发咨询和技术支持服务。 + +总的来说,Arcadia 是一家在游戏行业具有较高声誉和影响力的公司,他们不断推出新的游戏作品,为玩家带来更多精彩的游戏体验。 +``` + +#### Enable Similarity Search + +```shell +./bin/arctl chat --enable-embedding-search --chat-llm-apikey 26b2bc55fae40752055cadfc4792f9de.wagA4NIwg5aZJWhm --embedding-llm-apikey 26b2bc55fae40752055cadfc4792f9de.wagA4NIwg5aZJWhm --model chatglm_pro --num-docs 10 --question "介绍一下Arcadia" +``` + +Required Arguments: +- `--enable-embedding-search` +- `--embedding-llm-apikey` +- `--chat-llm-apikey` +- `--question` + +**Output:** +```shell +Prompts: [{user + 我将要询问一些问题,希望你仅使用我提供的上下文信息回答。 + 请不要在回答中添加其他信息。 + 若我提供的上下文不足以回答问题, + 请回复"我不确定",再做出适当的猜测。 + 请将回答内容分割为适于阅读的段落。 + } {assistant + 好的,我将尝试仅使用你提供的上下文信息回答,并在信息不足时提供一些合理推测。 + } {user 我的问题是: 介绍一下Arcadia. 以下是我提供的上下文:--- sidebar_position: 1 --- # 介绍## Contribute to Arcadia If you want to contribute to Arcadia, refer to [contribute guide](CONTRIBUTING.md). ## Support If you need support, start with the troubleshooting guide, or create GitHub [issues](https://github.com/kubeagi/arcadia/issues/new)```of developers contributing to its continued development and improvement. It is available on a variety of platforms, including Linux, Windows, and 移动设备,and can be deployed on-premises, in the cloud, or in a hybrid# Arcadia Our vision is to make it easier for cloud-native applications to integrate with AI, thereby making the cloud more intelligent and impactful. ## Quick start 1. Install arcadia operatormaintained by the Cloud Native Computing Foundation (CNCF).\\n\\nKubernetes provides a platform-as-a-service (PaaS) model, which allows developers to deploy, run, and scale containerized applications with minimal configuration and effort. It does this by abstracting the underlying infrastructure andunderlying infrastructure and providing a common set of APIs and tools that can be used to deploy, manage, and scale applications consistently across different environments.\\n\\nKubernetes is widely adopted by organizations of all sizes and has a large, active community of developers contributingin the cloud, or in a hybrid environment.\"","role":"assistant"}],"request_id":"7865480399259975113","task_id":"7865480399259975113","task_status":"SUCCESS","usage":{"total_tokens":203}},"msg":"操作成功","success":true}[内核](https://github.com/kubebb/core)基于[kubernetes## Packages To enhace the AI capability in Golang,we developed some packages. ### LLMs - ✅ [ZhiPuAI(智谱AI)](https://github.com/kubeagi/arcadia/tree/main/pkg/llms/zhipuai) - [example](https://github.com/kubeagi/arcadia/blob/main/examples/zhipuai/main.go) ### Embeddings}] + Arcadia 是一个项目,旨在使云原生应用程序更容易与 AI 集成,从而使云更加智能和有影响力。该项目由 Cloud Native Computing Foundation(CNCF)维护,并可在多种平台上使用,包括 Linux、Windows 和移动设备。它可以通过本地部署、云部署或混合部署。 + +Kubernetes 是一个提供平台即服务(PaaS)模型的系统,它使开发人员能够以最小的配置和努力来部署、运行和扩展容器化应用程序。它通过抽象底层基础设施,并提供一组通用的 API 和工具来实现这一目标,这些 API 和工具可以用于在不同的环境中一致地部署、管理和扩展应用程序。 + +Arcadia 项目包含了一些用于增强 Golang 中 AI 功能的软件包。其中,智谱 AI(ZhiPuAI)是一个 LLM(大型语言模型)软件包,提供了示例代码供开发者参考。 +``` + +## Limitations + +1. Vector Store + +- ✅ chromadb + +2. Document Types + +- ✅ text +- ✅ html +- ✅ csv + +3. Embedding Service + +- ✅ zhipuai +- ✅ openai + +4. LLM Service + +- ✅ zhipuai diff --git a/arctl/chat.go b/arctl/chat.go new file mode 100644 index 000000000..f1fab52ba --- /dev/null +++ b/arctl/chat.go @@ -0,0 +1,213 @@ +/* +Copyright 2023 KubeAGI. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "context" + "errors" + "fmt" + + zhipuaiembeddings "github.com/kubeagi/arcadia/pkg/embeddings/zhipuai" + "github.com/kubeagi/arcadia/pkg/llms" + "github.com/kubeagi/arcadia/pkg/llms/zhipuai" + "github.com/kubeagi/arcadia/pkg/vectorstores/chromadb" + "github.com/spf13/cobra" + "github.com/tmc/langchaingo/embeddings" + openaiEmbeddings "github.com/tmc/langchaingo/embeddings/openai" + "github.com/tmc/langchaingo/schema" + "github.com/tmc/langchaingo/vectorstores" +) + +var ( + question string + // chat with LLM + chatLLMType string + chatAPIKey string + model string + method string + temperature float32 + topP float32 + + // similarity search + enableSimilaritySearch bool + scoreThreshold float64 + numDocs int + + // promptsWithSimilaritySearch = []zhipuai.Prompt{ + // { + // Role: zhipuai.User, + // Content: `Hi there, I am going to ask you a question, which I would like you to answer + // based only on the provided context, and not any other information. + // If there is not enough information in the context to answer the question,'say \"I am not sure\", then try to make a guess.' + // Break your answer up into nicely readable paragraphs.`, + // }, + // { + // Role: zhipuai.Assistant, + // Content: "Sure.Please provide your documents.", + // }, + // } + promptsWithSimilaritySearchCN = []zhipuai.Prompt{ + { + Role: zhipuai.User, + Content: ` + 我将要询问一些问题,希望你仅使用我提供的上下文信息回答。 + 请不要在回答中添加其他信息。 + 若我提供的上下文不足以回答问题, + 请回复"我不确定",再做出适当的猜测。 + 请将回答内容分割为适于阅读的段落。 + `, + }, + { + Role: zhipuai.Assistant, + Content: ` + 好的,我将尝试仅使用你提供的上下文信息回答,并在信息不足时提供一些合理推测。 + `, + }, + } +) + +func NewChatCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "chat [usage]", + Short: "Do LLM chat with similarity search(optional)", + RunE: func(cmd *cobra.Command, args []string) error { + var docs []schema.Document + var err error + + if enableSimilaritySearch { + docs, err = SimilaritySearch(context.Background()) + if err != nil { + return err + } + fmt.Printf("similarDocs: %v \n", docs) + } + + return Chat(context.Background(), docs) + }, + } + + // For similarity search + cmd.Flags().BoolVar(&enableSimilaritySearch, "enable-embedding-search", false, "enable embedding similarity search") + cmd.Flags().StringVar(&embeddingLLMType, "embedding-llm-type", string(llms.ZhiPuAI), "llm type to use(Only zhipuai,openai supported now)") + cmd.Flags().StringVar(&embeddingLLMApiKey, "embedding-llm-apikey", "", "apiKey to access embedding service.Must required when embedding similarity search is enabled") + cmd.Flags().StringVar(&vectorStore, "vector-store", "http://localhost:8000", "vector stores to use(Only chroma supported now)") + cmd.Flags().StringVar(&nameSpace, "namespace", "arcadia", "namespace/collection to query from") + cmd.Flags().Float64Var(&scoreThreshold, "score-threshold", 0, "score threshold for similarity search(Higher is better)") + cmd.Flags().IntVar(&numDocs, "num-docs", 5, "number of documents to be returned with SimilarSearch") + + cmd.Flags().StringVar(&question, "question", "", "question text to be asked") + + // For LLM chat + cmd.Flags().StringVar(&chatLLMType, "chat-llm-type", string(llms.ZhiPuAI), "llm type to use(Only zhipuai,openai supported now)") + cmd.Flags().StringVar(&chatAPIKey, "chat-llm-apikey", "", "apiKey to access embedding service") + cmd.PersistentFlags().StringVar(&model, "model", string(zhipuai.ZhiPuAILite), "which model to use: chatglm_lite/chatglm_std/chatglm_pro") + cmd.PersistentFlags().StringVar(&method, "method", "sse-invoke", "Invoke method used when access LLM service(invoke/sse-invoke)") + cmd.PersistentFlags().Float32Var(&temperature, "temperature", 0.95, "temperature for chat") + cmd.PersistentFlags().Float32Var(&topP, "top-p", 0.7, "top-p for chat") + + cmd.MarkFlagRequired("chat-llm-apikey") + cmd.MarkFlagRequired("question") + + return cmd +} + +func SimilaritySearch(ctx context.Context) ([]schema.Document, error) { + var embedder embeddings.Embedder + var err error + + if embeddingLLMApiKey == "" { + return nil, errors.New("embedding-llm-apikey is required when embedding similarity search is enabled") + } + + switch embeddingLLMType { + case "zhipuai": + embedder, err = zhipuaiembeddings.NewZhiPuAI( + zhipuaiembeddings.WithClient(*zhipuai.NewZhiPuAI(embeddingLLMApiKey)), + ) + if err != nil { + return nil, err + } + case "openai": + embedder, err = openaiEmbeddings.NewOpenAI() + if err != nil { + return nil, err + } + default: + return nil, errors.New("unsupported embedding type") + } + + chroma, err := chromadb.New( + chromadb.WithURL(vectorStore), + chromadb.WithEmbedder(embedder), + chromadb.WithNameSpace(nameSpace), + ) + if err != nil { + return nil, err + } + + return chroma.SimilaritySearch(ctx, question, numDocs, vectorstores.WithScoreThreshold(scoreThreshold)) +} + +func Chat(ctx context.Context, similarDocs []schema.Document) error { + // Only for zhipuai + client := zhipuai.NewZhiPuAI(chatAPIKey) + + params := zhipuai.DefaultModelParams() + params.Model = zhipuai.Model(model) + params.Method = zhipuai.Method(method) + params.Temperature = temperature + params.TopP = topP + + var prompts []zhipuai.Prompt + if len(similarDocs) != 0 { + var docString string + for _, doc := range similarDocs { + docString += doc.PageContent + } + prompts = append(prompts, promptsWithSimilaritySearchCN...) + prompts = append(prompts, zhipuai.Prompt{ + Role: zhipuai.User, + Content: fmt.Sprintf("我的问题是: %s. 以下是我提供的上下文:%s", question, docString), + }) + } else { + prompts = append(prompts, zhipuai.Prompt{ + Role: zhipuai.User, + Content: question, + }) + } + + fmt.Printf("Prompts: %v \n", prompts) + params.Prompt = prompts + if params.Method == zhipuai.ZhiPuAIInvoke { + resp, err := client.Invoke(params) + if err != nil { + return err + } + if resp.Code != 200 { + return fmt.Errorf("chat failed: %s", resp.String()) + } + fmt.Println(resp.Data.Choices[0].Content) + return nil + } + + err := client.SSEInvoke(params, nil) + if err != nil { + return err + } + + return nil +} diff --git a/arctl/load.go b/arctl/load.go new file mode 100644 index 000000000..4646e9164 --- /dev/null +++ b/arctl/load.go @@ -0,0 +1,137 @@ +/* +Copyright 2023 KubeAGI. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "context" + "errors" + "os" + + zhipuaiembeddings "github.com/kubeagi/arcadia/pkg/embeddings/zhipuai" + "github.com/kubeagi/arcadia/pkg/llms" + "github.com/kubeagi/arcadia/pkg/llms/zhipuai" + "github.com/kubeagi/arcadia/pkg/vectorstores/chromadb" + "github.com/spf13/cobra" + "github.com/tmc/langchaingo/documentloaders" + "github.com/tmc/langchaingo/embeddings" + openaiEmbeddings "github.com/tmc/langchaingo/embeddings/openai" + "github.com/tmc/langchaingo/schema" + "github.com/tmc/langchaingo/textsplitter" +) + +var ( + embeddingLLMType string + embeddingLLMApiKey string + + document string + language string + + nameSpace string + chunkSize int + chunkOverlap int + + vectorStore string +) + +func NewLoadCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "load [usage]", + Short: "Load documents into VectorStore", + RunE: func(cmd *cobra.Command, args []string) error { + documents, err := LoadAndSplitDocument(context.Background()) + if err != nil { + return err + } + return EmbedDocuments(context.Background(), documents) + }, + } + + cmd.Flags().StringVar(&embeddingLLMType, "embedding-llm-type", string(llms.ZhiPuAI), "llm type to use(Only zhipuai,openai supported now)") + cmd.Flags().StringVar(&embeddingLLMApiKey, "embedding-llm-apikey", "", "apiKey to access embedding service") + + cmd.Flags().StringVar(&vectorStore, "vector-store", "http://localhost:8000", "vector stores to use(Only chroma supported now)") + + cmd.Flags().StringVar(&document, "document", "", "path of the document to load") + cmd.Flags().StringVar(&language, "document-language", "text", "language of the document(Only text,html,csv supported now)") + + cmd.Flags().StringVar(&nameSpace, "namespace", "arcadia", "namespace/collection of the document to load into") + cmd.Flags().IntVar(&chunkSize, "chunk-size", 300, "chunk size for embedding") + cmd.Flags().IntVar(&chunkOverlap, "chunk-overlap", 30, "chunk overlap for embedding") + + cmd.MarkFlagRequired("embedding-llm-apikey") + cmd.MarkFlagRequired("document") + + return cmd +} + +func LoadAndSplitDocument(ctx context.Context) ([]schema.Document, error) { + file, err := os.Open(document) + if err != nil { + return nil, err + } + + var loader documentloaders.Loader + switch language { + case "text": + loader = documentloaders.NewText(file) + case "csv": + loader = documentloaders.NewCSV(file) + case "html": + loader = documentloaders.NewHTML(file) + default: + return nil, errors.New("unsupported document language") + } + + split := textsplitter.NewRecursiveCharacter() + split.ChunkSize = chunkSize + split.ChunkOverlap = chunkOverlap + + return loader.LoadAndSplit(ctx, split) +} + +func EmbedDocuments(ctx context.Context, documents []schema.Document) error { + var embedder embeddings.Embedder + var err error + + switch embeddingLLMType { + case "zhipuai": + embedder, err = zhipuaiembeddings.NewZhiPuAI( + zhipuaiembeddings.WithClient(*zhipuai.NewZhiPuAI(embeddingLLMApiKey)), + ) + if err != nil { + return err + } + case "openai": + embedder, err = openaiEmbeddings.NewOpenAI() + if err != nil { + return err + } + default: + return errors.New("unsupported embedding type") + } + + chroma, err := chromadb.New( + chromadb.WithURL(vectorStore), + chromadb.WithEmbedder(embedder), + chromadb.WithNameSpace(nameSpace), + ) + if err != nil { + return err + } + + return chroma.AddDocuments(ctx, documents) +} diff --git a/arctl/main.go b/arctl/main.go new file mode 100644 index 000000000..f226cdfb8 --- /dev/null +++ b/arctl/main.go @@ -0,0 +1,42 @@ +/* +Copyright 2023 KubeAGI. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "github.com/spf13/cobra" +) + +func NewCLI() *cobra.Command { + arctl := &cobra.Command{ + Use: "arctl [usage]", + Short: "Command line tools for Arcadia", + RunE: func(cmd *cobra.Command, args []string) error { + return nil + }, + } + + arctl.AddCommand(NewLoadCmd()) + arctl.AddCommand(NewChatCmd()) + + return arctl +} + +func main() { + if err := NewCLI().Execute(); err != nil { + panic(err) + } +} diff --git a/go.mod b/go.mod index b0cac44d1..e7fd9a089 100644 --- a/go.mod +++ b/go.mod @@ -29,8 +29,11 @@ require ( github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect + github.com/PuerkitoBio/goquery v1.8.1 // indirect github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/andybalholm/cascadia v1.3.1 // indirect + github.com/aymerick/douceur v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -49,12 +52,15 @@ require ( github.com/google/gnostic v0.5.7-v3refs // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/gofuzz v1.1.0 // indirect + github.com/gorilla/css v1.0.0 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect + github.com/microcosm-cc/bluemonday v1.0.24 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect @@ -71,6 +77,7 @@ require ( go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.19.1 // indirect golang.org/x/crypto v0.12.0 // indirect + golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea // indirect golang.org/x/net v0.14.0 // indirect golang.org/x/oauth2 v0.10.0 // indirect golang.org/x/sys v0.11.0 // indirect diff --git a/go.sum b/go.sum index e1ac4b4e2..9089ecaf5 100644 --- a/go.sum +++ b/go.sum @@ -61,6 +61,8 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM= +github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= @@ -72,6 +74,8 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/amikos-tech/chroma-go v0.0.0-20230901221218-d0087270239e h1:RYla80qSHCeP8WL9Hyjfuk9n/KkDcYwzfaYbfc4cqRQ= github.com/amikos-tech/chroma-go v0.0.0-20230901221218-d0087270239e/go.mod h1:bPy9xmWK59Ix/nteEqIvPfAI0L07di+uZMb+RiYbles= +github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c= +github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -79,6 +83,8 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= +github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -270,6 +276,8 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= +github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= @@ -337,6 +345,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo= +github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -348,6 +358,8 @@ github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/microcosm-cc/bluemonday v1.0.24 h1:NGQoPtwGVcbGkKfvyYk1yRqknzBuoMiUrO6R7uFTPlw= +github.com/microcosm-cc/bluemonday v1.0.24/go.mod h1:ArQySAMps0790cHSkdPEJ7bGkF2VePWH773hsJNSHf8= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -490,6 +502,7 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= @@ -558,6 +571,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea h1:vLCWI/yYrdEHyN2JzIzPO3aaQJHQdp89IZBA/+azVC4= +golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -584,6 +599,7 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -632,9 +648,12 @@ golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1 golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -663,6 +682,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -730,10 +750,14 @@ golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -745,6 +769,7 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -812,6 +837,7 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.10-0.20220218145154-897bd77cd717/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=