Skip to content

Commit

Permalink
add version checker
Browse files Browse the repository at this point in the history
  • Loading branch information
juzeon committed Dec 5, 2023
1 parent 83f653a commit e9e4c54
Show file tree
Hide file tree
Showing 10 changed files with 478 additions and 230 deletions.
286 changes: 56 additions & 230 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,29 @@ package main

import (
"context"
_ "embed"
"encoding/base64"
"encoding/json"
"errors"
"github.com/PuerkitoBio/goquery"
"github.com/go-resty/resty/v2"
goversion "github.com/hashicorp/go-version"
"github.com/life4/genesis/slices"
"github.com/microcosm-cc/bluemonday"
"github.com/pkoukk/tiktoken-go"
"github.com/sashabaranov/go-openai"
"github.com/wailsapp/wails/v2/pkg/runtime"
"io"
"log/slog"
"os"
"path/filepath"
"regexp"
"strings"
"sydneyqt/sydney"
"sydneyqt/util"
"sync"
"time"
)

const (
_ = iota
AskTypeSydney
AskTypeOpenAI
)

type AskType int
//go:embed version.txt
var version string

// App struct
type App struct {
Expand All @@ -50,226 +44,6 @@ func (a *App) startup(ctx context.Context) {
a.ctx = ctx
}

type AskOptions struct {
Type AskType `json:"type"`
OpenAIBackend string `json:"openai_backend"`
ChatContext string `json:"chat_context"`
Prompt string `json:"prompt"`
ImageURL string `json:"image_url"`
}

const (
ChatFinishResultErrTypeMessageRevoke = "message_revoke"
ChatFinishResultErrTypeMessageFiltered = "message_filtered"
ChatFinishResultErrTypeOthers = "others"
)

type ChatFinishResult struct {
Success bool `json:"success"`
ErrType string `json:"err_type"`
ErrMsg string `json:"err_msg"`
}

const (
EventConversationCreated = "chat_conversation_created"
EventChatAppend = "chat_append"
EventChatFinish = "chat_finish"
EventChatSuggestedResponses = "chat_suggested_responses"
EventChatToken = "chat_token"
)

const (
EventChatStop = "chat_stop"
)

func (a *App) Dummy1() ChatFinishResult {
return ChatFinishResult{}
}
func (a *App) createSydney() (*sydney.Sydney, error) {
currentWorkspace, err := a.settings.config.GetCurrentWorkspace()
if err != nil {
return nil, err
}
cookies, err := util.ReadCookiesFile()
if err != nil {
return nil, err
}
return sydney.NewSydney(a.debug, cookies, a.settings.config.Proxy,
currentWorkspace.ConversationStyle, currentWorkspace.Locale, a.settings.config.WssDomain,
a.settings.config.CreateConversationURL,
currentWorkspace.NoSearch), nil
}

func (a *App) askSydney(options AskOptions) {
slog.Info("askSydney called", "options", options)
chatFinishResult := ChatFinishResult{
Success: true,
ErrType: "",
ErrMsg: "",
}
defer func() {
slog.Info("invoke EventChatFinish", "result", chatFinishResult)
runtime.EventsEmit(a.ctx, EventChatFinish, chatFinishResult)
}()
sydneyIns, err := a.createSydney()
if err != nil {
chatFinishResult = ChatFinishResult{
Success: false,
ErrType: ChatFinishResultErrTypeOthers,
ErrMsg: err.Error(),
}
return
}
conversation, err := sydneyIns.CreateConversation()
if err != nil {
chatFinishResult = ChatFinishResult{
Success: false,
ErrType: ChatFinishResultErrTypeOthers,
ErrMsg: err.Error(),
}
return
}
runtime.EventsEmit(a.ctx, EventConversationCreated)
stopCtx, cancel := util.CreateCancelContext()
defer cancel()
runtime.EventsOn(a.ctx, EventChatStop, func(optionalData ...interface{}) {
slog.Info("Received EventChatStop")
cancel()
})
ch := sydneyIns.AskStream(sydney.AskStreamOptions{
StopCtx: stopCtx,
Conversation: conversation,
Prompt: options.Prompt,
WebpageContext: options.ChatContext,
ImageURL: options.ImageURL,
})
chatAppend := func(text string) {
runtime.EventsEmit(a.ctx, EventChatAppend, text)
}
fullMessageText := ""
lastMessageType := ""
for msg := range ch {
textToAppend := ""
switch msg.Type {
case sydney.MessageTypeSuggestedResponses:
runtime.EventsEmit(a.ctx, EventChatSuggestedResponses, msg.Text)
case sydney.MessageTypeError:
if errors.Is(msg.Error, sydney.ErrMessageRevoke) {
chatFinishResult = ChatFinishResult{
Success: false,
ErrType: ChatFinishResultErrTypeMessageRevoke,
ErrMsg: msg.Error.Error(),
}
} else if errors.Is(msg.Error, sydney.ErrMessageFiltered) {
chatFinishResult = ChatFinishResult{
Success: false,
ErrType: ChatFinishResultErrTypeMessageFiltered,
ErrMsg: msg.Error.Error(),
}
} else {
chatFinishResult = ChatFinishResult{
Success: false,
ErrType: ChatFinishResultErrTypeOthers,
ErrMsg: msg.Error.Error(),
}
}
return
case sydney.MessageTypeMessageText:
fullMessageText += msg.Text
runtime.EventsEmit(a.ctx, EventChatToken, a.CountToken(fullMessageText))
textToAppend = msg.Text
default:
textToAppend = msg.Text + "\n\n"
}
if textToAppend != "" {
if lastMessageType != msg.Type {
textToAppend = "[assistant](#" + msg.Type + ")\n" + textToAppend
}
chatAppend(textToAppend)
}
lastMessageType = msg.Type
}
}
func (a *App) askOpenAI(options AskOptions) {
chatFinishResult := ChatFinishResult{
Success: true,
ErrType: "",
ErrMsg: "",
}
handleErr := func(err error) {
chatFinishResult = ChatFinishResult{
Success: false,
ErrType: ChatFinishResultErrTypeOthers,
ErrMsg: err.Error(),
}
}
defer func() {
slog.Info("invoke EventChatFinish", "result", chatFinishResult)
runtime.EventsEmit(a.ctx, EventChatFinish, chatFinishResult)
}()
backend, err := slices.Find(a.settings.config.OpenAIBackends, func(el OpenAIBackend) bool {
return el.Name == options.OpenAIBackend
})
if err != nil {
handleErr(err)
return
}
hClient, err := util.MakeHTTPClient(a.settings.config.Proxy, 0)
if err != nil {
handleErr(err)
return
}
config := openai.DefaultConfig(backend.OpenaiKey)
config.BaseURL = backend.OpenaiEndpoint
config.HTTPClient = hClient
client := openai.NewClientWithConfig(config)
messages := util.GetOpenAIChatMessages(options.ChatContext)
slog.Info("Get chat messages", "messages", messages)
messages = append(messages, openai.ChatCompletionMessage{
Role: "user",
Content: options.Prompt,
})
stream, err := client.CreateChatCompletionStream(context.Background(), openai.ChatCompletionRequest{
Model: backend.OpenaiShortModel,
Messages: messages,
Temperature: backend.OpenaiTemperature,
})
if err != nil {
handleErr(err)
return
}
runtime.EventsEmit(a.ctx, EventConversationCreated)
defer stream.Close()
fullMessage := ""
replied := false
for {
response, err := stream.Recv()
if errors.Is(err, io.EOF) {
slog.Info("openai chat completed")
return
}
if err != nil {
handleErr(err)
return
}
textToAppend := response.Choices[0].Delta.Content
fullMessage += textToAppend
runtime.EventsEmit(a.ctx, EventChatToken, a.CountToken(fullMessage))
if !replied {
textToAppend = "[assistant](#message)\n" + textToAppend
replied = true
}
runtime.EventsEmit(a.ctx, EventChatAppend, textToAppend)
}
}
func (a *App) AskAI(options AskOptions) {
if options.Type == AskTypeSydney {
a.askSydney(options)
} else if options.Type == AskTypeOpenAI {
a.askOpenAI(options)
}
}

var tk *tiktoken.Tiktoken
var initTkFunc = sync.OnceFunc(func() {
slog.Info("Init tiktoken")
Expand Down Expand Up @@ -422,3 +196,55 @@ func (a *App) GetUser() (string, error) {
}
return sydneyIns.GetUser()
}

type CheckUpdateResult struct {
NeedUpdate bool `json:"need_update"`
CurrentVersion string `json:"current_version"`
LatestVersion string `json:"latest_version"`
ReleaseURL string `json:"release_url"`
ReleaseNote string `json:"release_note"`
}

func (a *App) CheckUpdate() (CheckUpdateResult, error) {
empty := CheckUpdateResult{}
hClient, err := util.MakeHTTPClient(a.settings.config.Proxy, 0)
if err != nil {
return empty, err
}
client := resty.New().SetTimeout(15 * time.Second).SetTransport(hClient.Transport)
resp, err := client.R().Get("https://api.github.com/repos/juzeon/SydneyQt/releases")
if err != nil {
return empty, err
}
var githubRelease []GithubReleaseResponse
err = json.Unmarshal(resp.Body(), &githubRelease)
if err != nil {
return empty, err
}
if len(githubRelease) == 0 {
return empty, errors.New("no release found")
}
currentVersion, err := goversion.NewVersion(strings.TrimSpace(version))
if err != nil {
return empty, err
}
latestVersionStr := githubRelease[0].TagName
if strings.HasPrefix(latestVersionStr, "v") {
latestVersionStr = latestVersionStr[1:]
}
latestVersion, err := goversion.NewVersion(latestVersionStr)
if err != nil {
return empty, err
}
needUpdate := false
if latestVersion.GreaterThan(currentVersion) {
needUpdate = true
}
return CheckUpdateResult{
NeedUpdate: needUpdate,
CurrentVersion: currentVersion.String(),
LatestVersion: latestVersion.String(),
ReleaseURL: githubRelease[0].HtmlUrl,
ReleaseNote: githubRelease[0].Body,
}, nil
}
Loading

0 comments on commit e9e4c54

Please sign in to comment.