-
Notifications
You must be signed in to change notification settings - Fork 70
Added Go Server #17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Added Go Server #17
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
8be0615
Added go server
priankakariat 4be4456
Added readme for go server
priankakariat 95b2d7e
Fixed documentation and read me of go files
priankakariat a7bc461
Updated readme for go server
priankakariat 0231060
Updated documentation of json.go
priankakariat bb41cba
Updated formatting of license in go files
priankakariat 233135c
Added cors middleware handler to go server
priankakariat c2ffef4
Fixed readme for go server
priankakariat c0b4793
Update README.md
priankakariat e6ae3ac
Update README.md
priankakariat b6e7341
Update README.md to skip to manual install for Go
priankakariat 6b4c610
Update README.md
priankakariat f8cb2af
Update README.md
priankakariat a4d7518
Update README.md
priankakariat f2328a4
Update README.md
priankakariat 9e82320
Update README.md
priankakariat 9e383b5
Update README.md
priankakariat 873f294
Update README.md
priankakariat 7f68b8d
Update README.md
priankakariat 7235a45
Update README.md
priankakariat 61dbb0f
Update README.md
priankakariat af01e1f
Update README.md
priankakariat 54e4b23
Update README.md
priankakariat 46a2d48
Updated documentation of main.go
priankakariat 5c5a7fd
Merge branch 'server-go' of https://github.com/priankakariatyml/examp…
priankakariat File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Updated documentation of main.go
- Loading branch information
commit 46a2d4809e519ab5872179b2981a23c9fc907c9f
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,24 +33,25 @@ const modelName = "gemini-1.5-flash" | |
| const defaultPort = "9000" | ||
|
|
||
| // Server state holding the context of the Gemini client and the generative model. | ||
| type geminiServer struct { | ||
| type genaiServer struct { | ||
| ctx context.Context | ||
| model *genai.GenerativeModel | ||
| } | ||
|
|
||
| func main() { | ||
| ctx := context.Background() | ||
|
|
||
| // Access your API key as an environment variable to create a client. | ||
| apiKey := os.Getenv("GOOGLE_API_KEY") | ||
| client, err := genai.NewClient(ctx, option.WithAPIKey(apiKey)) | ||
| if err != nil { | ||
| log.Fatalf("Could not create Gemini client %v", err) | ||
| log.Fatalf("could not create Gemini client %v", err) | ||
| } | ||
| defer client.Close() | ||
|
|
||
| model := client.GenerativeModel(modelName) | ||
|
|
||
| server := &geminiServer{ | ||
| server := &genaiServer{ | ||
| ctx: ctx, | ||
| model: model, | ||
| } | ||
|
|
@@ -64,7 +65,6 @@ func main() { | |
| AllowedOrigins: []string{"*"}, | ||
| AllowedHeaders: []string{"Access-Control-Allow-Origin", "Content-Type"}, | ||
| }) | ||
|
|
||
| handler := c.Handler(mux) | ||
|
|
||
| // Access preferred port the server must listen to as an environment variable if provided. | ||
|
|
@@ -74,20 +74,26 @@ func main() { | |
| log.Fatal(http.ListenAndServe(addr, handler)) | ||
| } | ||
|
|
||
| // part is a piece of model content. It can hold only text pieces. Each item in the `Parts` of a | ||
| // JSON-encoded content in a history array must comply to it. | ||
| type part struct { | ||
| // Piece of model content. | ||
| Text string | ||
| } | ||
|
|
||
| // content is the structure to which each item in the incoming JSON-encoded history must comply to. | ||
| // content is the structure to which each item in the incoming JSON-encoded history array must | ||
| // comply to. | ||
| type content struct { | ||
| Role string | ||
| // The producer of the content. Must be either 'user' or 'model'. | ||
| Role string | ||
| // Ordered `Parts` that constitute a single message. | ||
| Parts []part | ||
| } | ||
|
|
||
| // chatRequest is the structure to which the incoming JSON-encoded value in the response body is | ||
| // decoded. | ||
| type chatRequest struct { | ||
| // The query from the user to the model and the history | ||
| // The query from the user to the model. | ||
| Chat string | ||
| // The history of the conversation between the user and the model in the current session. | ||
| History []content | ||
|
|
@@ -97,12 +103,12 @@ type chatRequest struct { | |
| // the request with the following format: | ||
| // Request: | ||
| // - chat: string | ||
| // - history: Array | ||
| // - history: [] | ||
| // | ||
| // Returns a JSON payload containing the model response with the following format. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is not actually returning anything, right? It's writing the response to |
||
| // Response: | ||
| // - text: string | ||
| func (gs *geminiServer) chatHandler(w http.ResponseWriter, r *http.Request) { | ||
| func (gs *genaiServer) chatHandler(w http.ResponseWriter, r *http.Request) { | ||
| cr := &chatRequest{} | ||
| if err := parseRequestJSON(r, cr); err != nil { | ||
| http.Error(w, err.Error(), http.StatusBadRequest) | ||
|
|
@@ -128,10 +134,10 @@ func (gs *geminiServer) chatHandler(w http.ResponseWriter, r *http.Request) { | |
| // JSON payload in the request with the following format: | ||
| // Request: | ||
| // - chat: string, | ||
| // - history: Array, | ||
| // - history: [], | ||
| // | ||
| // A partial response from the model is text. | ||
| func (gs *geminiServer) streamingChatHandler(w http.ResponseWriter, r *http.Request) { | ||
| // A partial response from the model contains a piece of text. | ||
| func (gs *genaiServer) streamingChatHandler(w http.ResponseWriter, r *http.Request) { | ||
| cr := &chatRequest{} | ||
| if err := parseRequestJSON(r, cr); err != nil { | ||
| http.Error(w, err.Error(), http.StatusBadRequest) | ||
|
|
@@ -149,13 +155,13 @@ func (gs *geminiServer) streamingChatHandler(w http.ResponseWriter, r *http.Requ | |
| break | ||
| } | ||
| if err != nil { | ||
| http.Error(w, err.Error(), http.StatusInternalServerError) | ||
| log.Println(err) | ||
| break | ||
| } | ||
|
|
||
| resTxt, err := responseString(res) | ||
| if err != nil { | ||
| http.Error(w, err.Error(), http.StatusInternalServerError) | ||
| log.Println(err) | ||
| break | ||
| } | ||
|
|
||
|
|
@@ -167,24 +173,24 @@ func (gs *geminiServer) streamingChatHandler(w http.ResponseWriter, r *http.Requ | |
| } | ||
|
|
||
| // startChat starts a chat session with the model using the given history. | ||
| func (gs *geminiServer) startChat(hist []content) *genai.ChatSession { | ||
| func (gs *genaiServer) startChat(hist []content) *genai.ChatSession { | ||
| cs := gs.model.StartChat() | ||
| cs.History = encodeHistory(hist) | ||
| cs.History = transform(hist) | ||
| return cs | ||
| } | ||
|
|
||
| // encodeHistory converts []content to a []*genai.Content that is accepted by the model's chat session. | ||
| func encodeHistory(cs []content) []*genai.Content { | ||
| // transform converts []content to a []*genai.Content that is accepted by the model's chat session. | ||
| func transform(cs []content) []*genai.Content { | ||
| gcs := make([]*genai.Content, len(cs)) | ||
| for i, c := range cs { | ||
| gcs[i] = c.geminiCompatible() | ||
| gcs[i] = c.transform() | ||
| } | ||
|
|
||
| return gcs | ||
| } | ||
|
|
||
| // geminiCompatible converts content to genai.Content accepted by the chat session. | ||
| func (c *content) geminiCompatible() *genai.Content { | ||
| // transform converts content to genai.Content that is accepted by the model's chat session. | ||
| func (c *content) transform() *genai.Content { | ||
| gc := &genai.Content{} | ||
| gc.Role = c.Role | ||
| ps := make([]genai.Part, len(c.Parts)) | ||
|
|
@@ -195,33 +201,34 @@ func (c *content) geminiCompatible() *genai.Content { | |
| return gc | ||
| } | ||
|
|
||
| // responseString parses the model response of type genai.GenerateContentResponse to a string. | ||
| func responseString(resp *genai.GenerateContentResponse) (string, error) { | ||
| // responseString converts the model response of type genai.GenerateContentResponse to a string. | ||
| func responseString(res *genai.GenerateContentResponse) (string, error) { | ||
| // Only taking the first candidate since GenerationConfig.CandidateCount defaults to 1. | ||
| if len(resp.Candidates) > 0 { | ||
| if cs := contentString(resp.Candidates[0].Content); cs != nil { | ||
| if len(res.Candidates) > 0 { | ||
| if cs := contentString(res.Candidates[0].Content); cs != nil { | ||
| return *cs, nil | ||
| } | ||
| } | ||
|
|
||
| return "", fmt.Errorf("invalid response from Gemini model") | ||
| } | ||
|
|
||
| // contentString converts genai.Content to a string. | ||
| // contentString converts genai.Content to a string. If the parts in the input content are of type | ||
| // text, they are concatenated with new lines in between them to form a string. | ||
| func contentString(c *genai.Content) *string { | ||
| if c == nil || c.Parts == nil { | ||
| return nil | ||
| } | ||
|
|
||
| contStrs := make([]string, len(c.Parts)) | ||
| cStrs := make([]string, len(c.Parts)) | ||
| for i, part := range c.Parts { | ||
| if pt, ok := part.(genai.Text); ok { | ||
| contStrs[i] = string(pt) | ||
| cStrs[i] = string(pt) | ||
| } else { | ||
| return nil | ||
| } | ||
| } | ||
|
|
||
| contStr := strings.Join(contStrs, "\n") | ||
| return &contStr | ||
| cStr := strings.Join(cStrs, "\n") | ||
| return &cStr | ||
| } | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't quite understand this, but it seems important: "Each item in the
Partsof a// JSON-encoded content in a history array must comply to it." Can you maybe clarify or rephrase?