geminicli_codeassist_client.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. package repository
  2. import (
  3. "context"
  4. "fmt"
  5. "time"
  6. "github.com/Wei-Shaw/sub2api/internal/pkg/geminicli"
  7. "github.com/Wei-Shaw/sub2api/internal/pkg/googleapi"
  8. "github.com/Wei-Shaw/sub2api/internal/service"
  9. "github.com/imroc/req/v3"
  10. )
  11. type geminiCliCodeAssistClient struct {
  12. baseURL string
  13. }
  14. func NewGeminiCliCodeAssistClient() service.GeminiCliCodeAssistClient {
  15. return &geminiCliCodeAssistClient{baseURL: geminicli.GeminiCliBaseURL}
  16. }
  17. func (c *geminiCliCodeAssistClient) LoadCodeAssist(ctx context.Context, accessToken, proxyURL string, reqBody *geminicli.LoadCodeAssistRequest) (*geminicli.LoadCodeAssistResponse, error) {
  18. if reqBody == nil {
  19. reqBody = defaultLoadCodeAssistRequest()
  20. }
  21. var out geminicli.LoadCodeAssistResponse
  22. client, err := createGeminiCliReqClient(proxyURL)
  23. if err != nil {
  24. return nil, fmt.Errorf("create HTTP client: %w", err)
  25. }
  26. resp, err := client.R().
  27. SetContext(ctx).
  28. SetHeader("Authorization", "Bearer "+accessToken).
  29. SetHeader("Content-Type", "application/json").
  30. SetHeader("User-Agent", geminicli.GeminiCLIUserAgent).
  31. SetBody(reqBody).
  32. SetSuccessResult(&out).
  33. Post(c.baseURL + "/v1internal:loadCodeAssist")
  34. if err != nil {
  35. fmt.Printf("[CodeAssist] LoadCodeAssist request error: %v\n", err)
  36. return nil, fmt.Errorf("request failed: %w", err)
  37. }
  38. if !resp.IsSuccessState() {
  39. body := resp.String()
  40. sanitizedBody := geminicli.SanitizeBodyForLogs(body)
  41. fmt.Printf("[CodeAssist] LoadCodeAssist failed: status %d, body: %s\n", resp.StatusCode, sanitizedBody)
  42. // Check if this is a SERVICE_DISABLED error and extract activation URL
  43. if googleapi.IsServiceDisabledError(body) {
  44. activationURL := googleapi.ExtractActivationURL(body)
  45. if activationURL != "" {
  46. return nil, fmt.Errorf("gemini API not enabled for this project, please enable it by visiting: %s\n\nAfter enabling the API, wait a few minutes for the changes to propagate, then try again", activationURL)
  47. }
  48. return nil, fmt.Errorf("gemini API not enabled for this project, please enable it in the Google Cloud Console at: https://console.cloud.google.com/apis/library/cloudaicompanion.googleapis.com")
  49. }
  50. return nil, fmt.Errorf("loadCodeAssist failed: status %d, body: %s", resp.StatusCode, sanitizedBody)
  51. }
  52. fmt.Printf("[CodeAssist] LoadCodeAssist success: status %d, response: %+v\n", resp.StatusCode, out)
  53. return &out, nil
  54. }
  55. func (c *geminiCliCodeAssistClient) OnboardUser(ctx context.Context, accessToken, proxyURL string, reqBody *geminicli.OnboardUserRequest) (*geminicli.OnboardUserResponse, error) {
  56. if reqBody == nil {
  57. reqBody = defaultOnboardUserRequest()
  58. }
  59. fmt.Printf("[CodeAssist] OnboardUser request body: %+v\n", reqBody)
  60. var out geminicli.OnboardUserResponse
  61. client, err := createGeminiCliReqClient(proxyURL)
  62. if err != nil {
  63. return nil, fmt.Errorf("create HTTP client: %w", err)
  64. }
  65. resp, err := client.R().
  66. SetContext(ctx).
  67. SetHeader("Authorization", "Bearer "+accessToken).
  68. SetHeader("Content-Type", "application/json").
  69. SetHeader("User-Agent", geminicli.GeminiCLIUserAgent).
  70. SetBody(reqBody).
  71. SetSuccessResult(&out).
  72. Post(c.baseURL + "/v1internal:onboardUser")
  73. if err != nil {
  74. fmt.Printf("[CodeAssist] OnboardUser request error: %v\n", err)
  75. return nil, fmt.Errorf("request failed: %w", err)
  76. }
  77. if !resp.IsSuccessState() {
  78. body := resp.String()
  79. sanitizedBody := geminicli.SanitizeBodyForLogs(body)
  80. fmt.Printf("[CodeAssist] OnboardUser failed: status %d, body: %s\n", resp.StatusCode, sanitizedBody)
  81. // Check if this is a SERVICE_DISABLED error and extract activation URL
  82. if googleapi.IsServiceDisabledError(body) {
  83. activationURL := googleapi.ExtractActivationURL(body)
  84. if activationURL != "" {
  85. return nil, fmt.Errorf("gemini API not enabled for this project, please enable it by visiting: %s\n\nAfter enabling the API, wait a few minutes for the changes to propagate, then try again", activationURL)
  86. }
  87. return nil, fmt.Errorf("gemini API not enabled for this project, please enable it in the Google Cloud Console at: https://console.cloud.google.com/apis/library/cloudaicompanion.googleapis.com")
  88. }
  89. return nil, fmt.Errorf("onboardUser failed: status %d, body: %s", resp.StatusCode, sanitizedBody)
  90. }
  91. fmt.Printf("[CodeAssist] OnboardUser success: status %d, response: %+v\n", resp.StatusCode, out)
  92. return &out, nil
  93. }
  94. func createGeminiCliReqClient(proxyURL string) (*req.Client, error) {
  95. return getSharedReqClient(reqClientOptions{
  96. ProxyURL: proxyURL,
  97. Timeout: 30 * time.Second,
  98. })
  99. }
  100. func defaultLoadCodeAssistRequest() *geminicli.LoadCodeAssistRequest {
  101. return &geminicli.LoadCodeAssistRequest{
  102. Metadata: geminicli.LoadCodeAssistMetadata{
  103. IDEType: "ANTIGRAVITY",
  104. Platform: "PLATFORM_UNSPECIFIED",
  105. PluginType: "GEMINI",
  106. },
  107. }
  108. }
  109. func defaultOnboardUserRequest() *geminicli.OnboardUserRequest {
  110. return &geminicli.OnboardUserRequest{
  111. TierID: "LEGACY",
  112. Metadata: geminicli.LoadCodeAssistMetadata{
  113. IDEType: "ANTIGRAVITY",
  114. Platform: "PLATFORM_UNSPECIFIED",
  115. PluginType: "GEMINI",
  116. },
  117. }
  118. }