[新手上路]批处理新手入门导读[视频教程]批处理基础视频教程[视频教程]VBS基础视频教程[批处理精品]批处理版照片整理器
[批处理精品]纯批处理备份&还原驱动[批处理精品]CMD命令50条不能说的秘密[在线下载]第三方命令行工具[在线帮助]VBScript / JScript 在线参考
返回列表 发帖

[网络工具] 命令行版的ChatGPT(修改版)

本帖最后由 CrLf 于 2023-7-29 00:17 编辑

命令行下调用OpenAI接口,从标准输入中读取用户输入并将其发送到GPT模型,再将响应写入标准输出。因原版默认是UTF8,所以我改成默认以GBK编码读取输入,并增加 --utf8 开关兼容utf8编码。

原版GitHub:https://github.com/pdfinn/sgpt

用法:
  1. sgpt -k <API_KEY> -i <INSTRUCTION> [-t TEMPERATURE] [-m MODEL] [-s SEPARATOR] [-u] [-d]
复制代码
参数说明:
短参数长参数 环境变量描述 默认值
-k --api_key SGPT_API_KEY 配置OpenAI的API KEY
-i --instruction SGPT_INSTRUCTION 系统指令,用于补充一些背景信息或要求
-t --temperature SGPT_TEMPERATURE 温度值,范围是0~1,数值越高,给出的答案越有想象力但也更倾向于编造 0.5
-m --model SGPT_MODEL 所采用的模型 gpt-3.5-turbo
-s --separator SGPT_SEPARATOR 不同内容的分隔符 \n
-u --utf8 SGPT_UTF8 以UTF8编码解读输入内容(该参数由CrLf添加,使默认编码是GBK) false
-d --debug SGPT_DEBUG 启用调试模式,将输出很多调试信息 false

CrLf修改后的源码:
  1. package main
  2. import (
  3. "bufio"
  4. "encoding/json"
  5. "fmt"
  6. "github.com/spf13/pflag"
  7. "github.com/spf13/viper"
  8. "io"
  9. "io/ioutil"
  10. "log"
  11. "net/http"
  12. "os"
  13. "strconv"
  14. "strings"
  15. // mod by CrLf 添加必要的模块
  16. "bytes"
  17. "golang.org/x/text/encoding/simplifiedchinese"
  18. "golang.org/x/text/transform"
  19. )
  20. // mod by CrLf 用于将UTF8转码为GBK
  21. // UTF-8 转 GBK
  22. func Utf8ToGbk(s []byte) ([]byte, error) {
  23. reader := transform.NewReader(bytes.NewReader(s), simplifiedchinese.GBK.NewEncoder())
  24. d, e := ioutil.ReadAll(reader)
  25. if e != nil {
  26. return nil, e
  27. }
  28. return d, nil
  29. }
  30. func GbkToUtf8(s []byte) ([]byte, error) {
  31.     reader := transform.NewReader(bytes.NewReader(s), simplifiedchinese.GBK.NewDecoder())
  32.     d, e := ioutil.ReadAll(reader)
  33.     if e != nil {
  34.         return nil, e
  35.     }
  36.     return d, nil
  37. }
  38. type OpenAIResponse struct {
  39. Choices []struct {
  40. Text    string `json:"text,omitempty"`
  41. Message struct {
  42. Role    string `json:"role,omitempty"`
  43. Content string `json:"content,omitempty"`
  44. } `json:"message,omitempty"`
  45. } `json:"choices"`
  46. }
  47. // mod by CrLf 声明utf8变量
  48. var utf8 *bool
  49. var debug *bool
  50. func init() {
  51. // mod by CrLf 去除重复的提醒
  52. // envUTF8 := os.Getenv("SGPT_UTF8")
  53. // envDebug := os.Getenv("SGPT_DEBUG")
  54. // utf8 = pflag.Bool("u", parseBoolWithDefault(envUTF8, false), "Enable UTF8 input")
  55. // debug = pflag.Bool("d", parseBoolWithDefault(envDebug, false), "Enable debug output")
  56. }
  57. func main() {
  58. // Default values
  59. defaultTemperature := 0.5
  60. defaultModel := "gpt-3.5-turbo"
  61. // Check environment variables
  62. envApiKey := os.Getenv("SGPT_API_KEY")
  63. envInstruction := os.Getenv("SGPT_INSTRUCTION")
  64. envTemperature, err := strconv.ParseFloat(os.Getenv("SGPT_TEMPERATURE"), 64)
  65. if err != nil {
  66. envTemperature = defaultTemperature
  67. }
  68. envModel := os.Getenv("SGPT_MODEL")
  69. envSeparator := os.Getenv("SGPT_SEPARATOR")
  70. // mod by CrLf 增加对环境变量 SGPT_UTF8 的支持
  71. envUTF8 := parseBoolWithDefault(os.Getenv("SGPT_UTF8"), false)
  72. envDebug := parseBoolWithDefault(os.Getenv("SGPT_DEBUG"), false)
  73. // Command line arguments
  74. apiKey := pflag.StringP("api_key", "k", envApiKey, "OpenAI API key")
  75. instruction := pflag.StringP("instruction", "i", envInstruction, "Instruction for the GPT model")
  76. temperature := pflag.Float64P("temperature", "t", envTemperature, "Temperature for the GPT model")
  77. model := pflag.StringP("model", "m", envModel, "GPT model to use")
  78. defaulSeparator := "\n"
  79. separator := pflag.StringP("separator", "s", envSeparator, "Separator character for input")
  80. if *separator == "" {
  81. *separator = defaulSeparator
  82. }
  83. // mod by CrLf 增加对参数 --utf8 或 -u 的支持
  84. utf8 = pflag.BoolP("utf8", "u", envUTF8, "Enable UTF8 input")
  85. debug = pflag.BoolP("debug", "d", envDebug, "Enable debug output")
  86. pflag.Parse()
  87. // Read the configuration file
  88. viper.SetConfigName("sgpt")
  89. viper.AddConfigPath(".")
  90. viper.AddConfigPath("$HOME/.sgpt")
  91. viper.SetConfigType("yaml")
  92. err = viper.ReadInConfig()
  93. // mod by CrLf 默认屏蔽无用警告,仅在debug模式下展示
  94. if _, ok := err.(viper.ConfigFileNotFoundError); ok {
  95. debugOutput(*debug, "Warning: Config file not found: %v", err)
  96. } else if err != nil {
  97. debugOutput(*debug, "Warning: Error reading config file: %v", err)
  98. }
  99. // Set default values and bind configuration values to flags
  100. viper.SetDefault("model", defaultModel)
  101. viper.SetDefault("temperature", defaultTemperature)
  102. viper.BindPFlag("api_key", pflag.Lookup("k"))
  103. viper.BindPFlag("instruction", pflag.Lookup("i"))
  104. viper.BindPFlag("model", pflag.Lookup("m"))
  105. viper.BindPFlag("temperature", pflag.Lookup("t"))
  106. viper.BindPFlag("separator", pflag.Lookup("s"))
  107. viper.BindPFlag("debug", pflag.Lookup("d"))
  108. // Use default values if neither flags nor environment variables are set
  109. if *model == "" {
  110. *model = defaultModel
  111. }
  112. if *apiKey == "" {
  113. log.Fatal("API key is required")
  114. }
  115. // Read input from stdin continuously
  116. // mod by CrLf 根据utf8开关的启禁用状态判断以utf8还是gbk读取stdin
  117. var reader io.RuneReader
  118. if *utf8 {
  119. reader = bufio.NewReader(os.Stdin)
  120. } else {
  121. byteInput, _ := io.ReadAll(os.Stdin)
  122. gbkBytes, _ := GbkToUtf8(byteInput)
  123. reader = bytes.NewReader(gbkBytes)
  124. }
  125. var inputBuffer strings.Builder
  126. for {
  127. inputChar, _, err := reader.ReadRune()
  128. if err == io.EOF {
  129. input := inputBuffer.String()
  130. if input != "" {
  131. response, err := callOpenAI(*apiKey, *instruction, input, *temperature, *model)
  132. if err != nil {
  133. log.Fatal(err)
  134. }
  135. fmt.Println(response)
  136. }
  137. break
  138. }
  139. if err != nil {
  140. log.Fatal(err)
  141. }
  142. if string(inputChar) == *separator {
  143. input := inputBuffer.String()
  144. inputBuffer.Reset()
  145. response, err := callOpenAI(*apiKey, *instruction, input, *temperature, *model)
  146. if err != nil {
  147. log.Fatal(err)
  148. }
  149. fmt.Println(response)
  150. } else {
  151. inputBuffer.WriteRune(inputChar)
  152. }
  153. }
  154. }
  155. func debugOutput(debug bool, format string, a ...interface{}) {
  156. if debug {
  157. log.Printf(format, a...)
  158. }
  159. }
  160. func parseFloatWithDefault(value string, defaultValue float64) float64 {
  161. if value == "" {
  162. return defaultValue
  163. }
  164. parsedValue, err := strconv.ParseFloat(value, 64)
  165. if err != nil {
  166. log.Printf("Warning: Failed to parse float value: %v", err)
  167. return defaultValue
  168. }
  169. return parsedValue
  170. }
  171. func parseBoolWithDefault(value string, defaultValue bool) bool {
  172. if value == "" {
  173. return defaultValue
  174. }
  175. parsedValue, err := strconv.ParseBool(value)
  176. if err != nil {
  177. log.Printf("Warning: Failed to parse bool value: %v", err)
  178. return defaultValue
  179. }
  180. return parsedValue
  181. }
  182. func callOpenAI(apiKey, instruction, input string, temperature float64, model string) (string, error) {
  183. var url string
  184. var jsonData []byte
  185. var err error
  186. switch model {
  187. case "gpt-4", "gpt-4-0314", "gpt-4-32k", "gpt-4-32k-0314", "gpt-3.5-turbo":
  188. url = "https://api.openai.com/v1/chat/completions"
  189. // Prepare JSON data for GPT-4 models
  190. messages := []map[string]string{
  191. {"role": "system", "content": instruction},
  192. {"role": "user", "content": input},
  193. }
  194. jsonData, err = json.Marshal(map[string]interface{}{
  195. "model":       model,
  196. "messages":    messages,
  197. "temperature": temperature,
  198. "max_tokens":  100,
  199. "stop":        []string{"\n"},
  200. })
  201. case "text-davinci-003", "text-davinci-002", "text-curie-001", "text-babbage-001", "text-ada-001":
  202. url = "https://api.openai.com/v1/completions"
  203. // Prepare JSON data for GPT-3 models
  204. prompt := instruction + " " + input
  205. jsonData, err = json.Marshal(map[string]interface{}{
  206. "model":       model,
  207. "prompt":      prompt,
  208. "temperature": temperature,
  209. "max_tokens":  100,
  210. "stop":        []string{"\n"},
  211. })
  212. case "whisper-1":
  213. url = "https://api.openai.com/v1/audio/transcriptions"
  214. default:
  215. return "", fmt.Errorf("unsupported model: %s", model)
  216. }
  217. if err != nil {
  218. return "", err
  219. }
  220. data := strings.NewReader(string(jsonData))
  221. req, err := http.NewRequest("POST", url, data)
  222. if err != nil {
  223. return "", err
  224. }
  225. req.Header.Set("Content-Type", "application/json")
  226. req.Header.Set("Authorization", "Bearer "+apiKey)
  227. client := &http.Client{}
  228. resp, err := client.Do(req)
  229. if err != nil {
  230. return "", err
  231. }
  232. defer resp.Body.Close()
  233. body, err := ioutil.ReadAll(resp.Body)
  234. if err != nil {
  235. return "", err
  236. }
  237. debugOutput(*debug, "API response: %s\n", string(body))
  238. var openAIResponse OpenAIResponse
  239. err = json.Unmarshal(body, &openAIResponse)
  240. if err != nil {
  241. return "", err
  242. }
  243. if len(openAIResponse.Choices) == 0 {
  244. debugOutput(*debug, "API response: %s\n", string(body))
  245. debugOutput(*debug, "HTTP status code: %s\n", strconv.Itoa(resp.StatusCode))
  246. return "", fmt.Errorf("no choices returned from the API")
  247. }
  248. assistantMessage := ""
  249. for _, choice := range openAIResponse.Choices {
  250. if choice.Message.Role == "assistant" {
  251. assistantMessage = strings.TrimSpace(choice.Message.Content)
  252. break
  253. }
  254. if choice.Text != "" {
  255. assistantMessage = strings.TrimSpace(choice.Text)
  256. break
  257. }
  258. }
  259. if assistantMessage == "" {
  260. return "", fmt.Errorf("no assistant message found in the API response")
  261. }
  262. return assistantMessage, nil
  263. }
复制代码
编译后的下载地址:http://bcn.bathome.net/s/tool/index.html?key=sgpt
1

评分人数

本帖最后由 CrLf 于 2023-7-29 00:13 编辑

举个例子:
  1. echo 柬埔寨在哪里|sgpt.exe --api_key "***这里是你的openai_api_key***" --instruction "请用中文回答:" --model "gpt-3.5-turbo"
  2. :: 回答为:柬埔寨位于东南亚,东临越南,南接泰国,西邻泰国和洞朗,北界老挝。
复制代码
如果要传入非GBK字符,请 chcp 65001 后使用 --utf8 开关

TOP

回复 2# CrLf


    感谢大佬分享, 请教一下,
1. APIkey能不挂梯直接在国内用吗? 会被封吗号?
2. 免费的帐号创建的api能直接用吗? 听说好像有几$的额度, 我试了一下能创建key, 但是没有看到额度,
先感谢

TOP

返回列表