使用Fyne构建Go语言OpenAI API中转测试工具

前言

在这篇博客中,我们将介绍如何使用Go语言和Fyne框架构建一个简易的API工具。该工具允许用户输入API URL和API Key,通过简易的GUI与API进行交互,获取账单信息、模型列表并测试模型。本项目展示了一些实用的Go编程技巧和Fyne的使用方法。

完整代码下载地址在文末!!!!

项目依赖

首先,我们需要确保已经安装了必要的依赖项:

  • Go编程语言
  • Fyne GUI框架

你可以通过如下命令来安装Fyne:

go get fyne.io/fyne/v2

项目结构

项目的主代码入口在main.go文件中,其核心组件包括以下几部分:

  • GUI组件初始化与布局
  • API请求及响应处理
  • 输入验证
  • 结果展示
    在这里插入图片描述

主函数

主函数初始化应用,并设置窗口和各个UI组件。

func main() {
    a := app.New()
    w := a.NewWindow("API工具")
    w.Resize(fyne.NewSize(600, 600))

    // 创建输入框和输出框
    apiURL := widget.NewEntry()
    apiURL.SetPlaceHolder("请输入API URL")
    
    apiKey := widget.NewEntry()
    apiKey.SetPlaceHolder("请输入您的API Key")

    output := widget.NewMultiLineEntry()
    output.SetPlaceHolder("输出将在这里展示...\n")
    output.SetMinRowsVisible(15)

    // 创建按钮
    getBalance := widget.NewButton("获取余额", func() {
        handleGetBalance(apiURL.Text, apiKey.Text, output, w)
    })

    getModels := widget.NewButton("获取模型列表", func() {
        handleGetModels(apiURL.Text, apiKey.Text, output, w)
    })

    testModel := widget.NewButton("测试模型", func() {
        showTestModelDialog(apiURL.Text, apiKey.Text, output, w)
    })

    // 布局组件
    layout := container.NewVBox(
        widget.NewLabel("API URL:"),
        apiURL,
        widget.NewLabel("API Key:"),
        apiKey,
        getBalance,
        getModels,
        testModel,
        container.NewMax(output),
    )

    w.SetContent(layout)
    w.ShowAndRun()
}

输入验证

为了确保API请求能够成功,我们需要对用户输入的API URL和API Key进行验证。

func validateInputs(apiURL, apiKey string) error {
    if apiURL == "" || apiKey == "" {
        return fmt.Errorf("请填写API URL和API Key")
    }
    return nil
}

获取余额

该功能涉及两个API请求:一个用于获取订阅信息,另一个用于获取账单信息。我们将这些内容展示在输出框中。

func handleGetBalance(apiURL, apiKey string, output *widget.Entry, w fyne.Window) {
    if err := validateInputs(apiURL, apiKey); err != nil {
        dialog.ShowError(err, w)
        return
    }

    // 获取订阅信息
    url := fmt.Sprintf("%s/v1/dashboard/billing/subscription", strings.TrimSpace(apiURL))
    req, _ := http.NewRequest("GET", url, nil)
    req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", strings.TrimSpace(apiKey)))
    req.Header.Set("Content-Type", "application/json")

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        dialog.ShowError(fmt.Errorf("请求失败: %v", err), w)
        return
    }
    defer resp.Body.Close()

    body, _ := ioutil.ReadAll(resp.Body)
    var data SubscriptionData
    if err := json.Unmarshal(body, &data); err != nil {
        dialog.ShowError(fmt.Errorf("解析响应失败: %v", err), w)
        return
    }

    // 获取账单信息
    startDate := "2021-01-01"
    endDate := "2022-01-01"
    billingURL := fmt.Sprintf("%s/v1/dashboard/billing/usage?start_date=%s&end_date=%s", strings.TrimSpace(apiURL), startDate, endDate)
    req, _ = http.NewRequest("GET", billingURL, nil)
    req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", strings.TrimSpace(apiKey)))

    resp, err = client.Do(req)
    if err != nil {
        dialog.ShowError(fmt.Errorf("请求失败: %v", err), w)
        return
    }
    defer resp.Body.Close()

    billingBody, _ := ioutil.ReadAll(resp.Body)
    var billingData BillingData
    if err := json.Unmarshal(billingBody, &billingData); err != nil {
        dialog.ShowError(fmt.Errorf("解析响应失败: %v", err), w)
        return
    }

    remaining := data.HardLimitUSD - billingData.TotalUsage/100
    text := fmt.Sprintf("总额: %.4f USD\n已用: %.4f USD\n剩余: %.4f USD", data.HardLimitUSD, billingData.TotalUsage/100, remaining)
    output.SetText(text)
}

获取模型列表

该功能通过API请求获取模型列表,并将结果展示在输出框中。

func handleGetModels(apiURL, apiKey string, output *widget.Entry, w fyne.Window) {
    if err := validateInputs(apiURL, apiKey); err != nil {
        dialog.ShowError(err, w)
        return
    }

    url := fmt.Sprintf("%s/v1/models", strings.TrimSpace(apiURL))
    req, _ := http.NewRequest("GET", url, nil)
    req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", strings.TrimSpace(apiKey)))

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        dialog.ShowError(fmt.Errorf("请求失败: %v", err), w)
        return
    }
    defer resp.Body.Close()

    body, _ := ioutil.ReadAll(resp.Body)
    var modelData ModelData
    if err := json.Unmarshal(body, &modelData); err != nil {
        dialog.ShowError(fmt.Errorf("解析响应失败: %v", err), w)
        return
    }

    models := make([]string, len(modelData.Data))
    for i, model := range modelData.Data {
        models[i] = model.ID
    }
    text := fmt.Sprintf("模型列表:\n%s", strings.Join(models, "\n"))
    output.SetText(text)
}

测试模型

该功能展示了一个弹出的对话框,允许用户输入模型名称和选择是否返回完整的响应信息。

func showTestModelDialog(apiURL, apiKey string, output *widget.Entry, w fyne.Window) {
    modelNameEntry := widget.NewEntry()
    modelNameEntry.SetPlaceHolder("模型名称 (gpt-3.5-turbo)")

    fullResponseCheckbox := widget.NewCheck("返回完整信息", nil)

    var modal *widget.PopUp
    submitButton := widget.NewButton("提交", func() {
        modelName := modelNameEntry.Text
        if modelName == "" {
            modelName = "gpt-3.5-turbo"
        }
        testModelRequest(apiURL, apiKey, modelName, fullResponseCheckbox.Checked, output, w)
        if modal != nil {
            modal.Hide()
        }
    })

    content := container.NewVBox(
        widget.NewLabel("请输入模型名称 (默认使用 gpt-3.5-turbo):"),
        modelNameEntry,
        fullResponseCheckbox,
    )

    closeButton := widget.NewButton("关闭", func() {
        if modal != nil {
            modal.Hide()
        }
    })

    content.Add(container.NewHBox(submitButton, closeButton))

    modal = widget.NewModalPopUp(content, w.Canvas())
    modal.Resize(fyne.NewSize(300, 200))
    modal.Show()
}

func testModelRequest(apiURL, apiKey, modelName string, fullResponse bool, output *widget.Entry, w fyne.Window) {
    if err := validateInputs(apiURL, apiKey); err != nil {
        dialog.ShowError(err, w)
        return
    }

    url := fmt.Sprintf("%s/v1/chat/completions", strings.TrimSpace(apiURL))
    data := map[string]interface{}{
        "model": modelName,
        "messages": []map[string]string{
            {"role": "user", "content": "say this is a test!"},
        },
    }
    jsonData, _ := json.Marshal(data)

    req, _ := http.NewRequest("POST", url, strings.NewReader(string(jsonData)))
    req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", strings.TrimSpace(apiKey)))
    req.Header.Set("Content-Type", "application/json")

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        dialog.ShowError(fmt.Errorf("请求失败: %v", err), w)
        return
    }
    defer resp.Body.Close()

    body, _ := ioutil.ReadAll(resp.Body)
    var completionResponse CompletionResponse
    if err := json.Unmarshal(body, &completionResponse); err != nil {
        dialog.ShowError(fmt.Errorf("解析响应失败: %v", err), w)
        return
    }

    if fullResponse {
        var prettyJSON bytes.Buffer
        error := json.Indent(&prettyJSON, body, "", "  ")
        if error != nil {
            output.SetText(fmt.Sprintf("格式化JSON失败: %v", error))
        } else {
            output.SetText(prettyJSON.String())
        }
    } else if len(completionResponse.Choices) > 0 {
        output.SetText(completionResponse.Choices[0].Message.Content)
    } else {
        output.SetText("未收到模型回应")
    }
}

技术要点

1. GUI组件的使用

Fyne 提供了一系列丰富的控件,比如 Entry, ButtonMultiLineEntry。通过配置这些控件,我们可以迅速搭建一个简单且实用的GUI界面。

2. 网络请求与JSON解析

利用 net/http 包来处理HTTP请求。通过自定义请求头和API地址,实现与服务端的数据交互。encoding/json 包用于解析和生成JSON数据,确保了数据格式的一致性。

3. 错误处理与用户反馈

采用对话框(dialog)来显示错误信息,增强了用户体验。当请求失败或JSON解析出错时,能够及时反馈给用户,减少用户的疑惑。

4. 弹出对话框与动态内容

使用 widget.NewModalPopUp 创建弹出对话框,使得应用更加灵活。用户可以输入具体的模型名称并选择是否需要完整的响应信息,这种动态交互增强了程序的自适应能力。

运行与调试

  1. 在你的Go项目目录中创建一个main.go文件,并将前文的代码粘贴进去。
  2. 确保你已经安装了Fyne框架。
  3. 运行你的程序:
go run main.go
  1. 按照提示输入API URL和API Key,然后点击各个按钮进行相应操作。

结尾

完整代码地址:https://github.com/stfghly/test-all-api/blob/main/test.go

通过这篇博客,我们了解了如何使用Go语言和Fyne构建一个简易的API工具。这不仅展示了Go语言强大的并发和网络处理能力,同时也展示了Fyne框架在构建桌面应用时的便利性。希望这篇博客对你有所帮助,如果你有任何问题或建议,欢迎在评论区交流讨论。
在这里插入图片描述

  • 39
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值