-
Notifications
You must be signed in to change notification settings - Fork 3
feat: Add Jules Agent SDK for Go #4
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
base: main
Are you sure you want to change the base?
feat: Add Jules Agent SDK for Go #4
Conversation
This commit introduces a complete Go SDK equivalent to the existing Python SDK for the Jules Agent API. Features: - Sessions API: Create and manage AI agent sessions with full lifecycle support - Activities API: Track and retrieve session activities with automatic pagination - Sources API: Manage source repositories and retrieve repository information - Comprehensive error handling with specific error types (Authentication, NotFound, Validation, RateLimit, Server) - Automatic retry logic with exponential backoff for failed requests - HTTP connection pooling for efficient resource management - Context support for cancellation and timeouts - Wait for completion with configurable polling intervals Project Structure: - jules/: Core SDK package with all API clients and models - examples/: Sample code demonstrating SDK usage - README.md: Comprehensive documentation with examples - go.mod: Go module definition The SDK follows Go best practices and provides the same functionality as the Python SDK with idiomatic Go patterns including: - Struct-based configuration - Context for request lifecycle management - Error wrapping for better error handling - Interfaces for extensibility Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Summary of ChangesHello @sashimikun, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request delivers a new Go SDK for the Jules Agent API, enabling Go developers to seamlessly interact with AI agent sessions, track activities, and manage source repositories. The SDK is built with robustness in mind, featuring automatic retries, connection pooling, and a structured error handling mechanism. It adheres to Go's best practices, ensuring an idiomatic and developer-friendly experience, and comes with thorough documentation and examples to facilitate quick adoption. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
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.
Code Review
This pull request introduces a comprehensive Go SDK for the Jules Agent API. The structure is well-organized, and the features cover the necessary API interactions, including sessions, activities, and sources. The implementation includes good practices like context support, connection pooling, and automatic retries.
My review focuses on improving robustness, performance, and adherence to Go idioms. Key areas for improvement include fixing a critical resource leak in the HTTP client's retry loop, making query parameter construction safer, and improving the efficiency of response parsing. I've also noted a potential panic in one of the examples and a non-standard Go module version. Overall, this is a solid foundation for the SDK, and addressing these points will make it even more reliable and efficient.
| defer resp.Body.Close() | ||
| respBody, err := io.ReadAll(resp.Body) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to read response body: %w", err) | ||
| } |
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.
Using defer resp.Body.Close() inside a loop is a classic Go pitfall that can lead to resource leaks. The deferred call will only execute when the request function returns, not at the end of each loop iteration. If a request is retried (e.g., on a 5xx error), the connection from the previous attempt will remain open. The response body should be closed explicitly within the loop, immediately after it has been read.
| defer resp.Body.Close() | |
| respBody, err := io.ReadAll(resp.Body) | |
| if err != nil { | |
| return nil, fmt.Errorf("failed to read response body: %w", err) | |
| } | |
| respBody, err := io.ReadAll(resp.Body) | |
| resp.Body.Close() | |
| if err != nil { | |
| return nil, fmt.Errorf("failed to read response body: %w", err) | |
| } |
| @@ -0,0 +1,3 @@ | |||
| module github.com/sashimikun/jules-agent-sdk-go | |||
|
|
|||
| go 1.24.7 | |||
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.
| fmt.Println("\n=== Creating Session ===") | ||
| session, err := client.Sessions.Create(ctx, &jules.CreateSessionRequest{ | ||
| Prompt: "Add a new feature to improve error handling", | ||
| Source: sources[0].Name, // Use the first source |
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.
|
|
||
| // Parse response | ||
| var result ActivitiesListResponse | ||
| respBytes, _ := json.Marshal(resp) |
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.
The error from json.Marshal is being ignored. While marshalling a map[string]interface{} is unlikely to fail here, it's best practice to handle all errors. This pattern of ignoring the marshal error is also present in the List methods of jules/sessions.go and jules/sources.go.
| respBytes, _ := json.Marshal(resp) | |
| respBytes, err := json.Marshal(resp) | |
| if err != nil { | |
| return nil, fmt.Errorf("failed to marshal activities list: %w", err) | |
| } |
| func (a *ActivitiesAPI) parseActivity(data map[string]interface{}) (*Activity, error) { | ||
| // Convert to JSON and back to properly parse the activity | ||
| jsonBytes, err := json.Marshal(data) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to marshal activity data: %w", err) | ||
| } | ||
|
|
||
| var activity Activity | ||
| if err := json.Unmarshal(jsonBytes, &activity); err != nil { | ||
| return nil, fmt.Errorf("failed to parse activity: %w", err) | ||
| } | ||
|
|
||
| return &activity, nil | ||
| } |
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.
The map -> JSON -> struct conversion pattern used here is inefficient as it involves extra memory allocations and CPU cycles for marshalling and unmarshalling. A more performant approach would be to decode the map into the struct directly using a library like mitchellh/mapstructure. This same pattern is used in parseSession and parseSource and could be improved there as well.
| func (s *SessionsAPI) Create(ctx context.Context, req *CreateSessionRequest) (*Session, error) { | ||
| // Build request body | ||
| body := map[string]interface{}{ | ||
| "prompt": req.Prompt, | ||
| "sourceContext": map[string]interface{}{ | ||
| "source": req.Source, | ||
| }, | ||
| } | ||
|
|
||
| if req.StartingBranch != "" { | ||
| sourceContext := body["sourceContext"].(map[string]interface{}) | ||
| sourceContext["githubRepoContext"] = map[string]interface{}{ | ||
| "startingBranch": req.StartingBranch, | ||
| } | ||
| } | ||
|
|
||
| if req.Title != "" { | ||
| body["title"] = req.Title | ||
| } | ||
|
|
||
| if req.RequirePlanApproval { | ||
| body["requirePlanApproval"] = true | ||
| } |
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.
Building the request body using map[string]interface{} and type assertions like body["sourceContext"].(map[string]interface{}) is fragile and not type-safe. A change in the map structure could lead to a runtime panic. It would be more robust to define a private struct for the request body and marshal that to JSON. This improves type safety, readability, and maintainability.
|
|
||
| // Parse response | ||
| var result SessionsListResponse | ||
| respBytes, _ := json.Marshal(resp) |
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.
The error from json.Marshal is being ignored. While marshalling a map[string]interface{} is unlikely to fail here, it's best practice to handle all errors.
| respBytes, _ := json.Marshal(resp) | |
| respBytes, err := json.Marshal(resp) | |
| if err != nil { | |
| return nil, fmt.Errorf("failed to marshal sessions list: %w", err) | |
| } |
| if opts != nil { | ||
| query := "" | ||
| if opts.Filter != "" { | ||
| query += fmt.Sprintf("filter=%s", opts.Filter) | ||
| } | ||
| if opts.PageSize > 0 { | ||
| if query != "" { | ||
| query += "&" | ||
| } | ||
| query += fmt.Sprintf("pageSize=%d", opts.PageSize) | ||
| } | ||
| if opts.PageToken != "" { | ||
| if query != "" { | ||
| query += "&" | ||
| } | ||
| query += fmt.Sprintf("pageToken=%s", opts.PageToken) | ||
| } | ||
| if query != "" { | ||
| path += "?" + query | ||
| } | ||
| } |
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.
Building query strings manually by concatenating strings is fragile and can be error-prone, especially with multiple optional parameters. Using the net/url package provides a more robust and idiomatic way to construct query parameters. This approach also improves readability. This feedback also applies to the List methods in jules/activities.go and jules/sessions.go. You'll need to add "net/url" to your imports for this change.
if opts != nil {
params := url.Values{}
if opts.Filter != "" {
params.Set("filter", opts.Filter)
}
if opts.PageSize > 0 {
params.Set("pageSize", fmt.Sprintf("%d", opts.PageSize))
}
if opts.PageToken != "" {
params.Set("pageToken", opts.PageToken)
}
if len(params) > 0 {
path += "?" + params.Encode()
}
}|
|
||
| // Parse response | ||
| var result SourcesListResponse | ||
| respBytes, _ := json.Marshal(resp) |
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.
This commit introduces a complete Go SDK equivalent to the existing Python SDK for the Jules Agent API.
Features:
Project Structure:
The SDK follows Go best practices and provides the same functionality as the Python SDK with idiomatic Go patterns including:
Generated with Claude Code