ballerina-platform / ballerina-library

The Ballerina Library
https://ballerina.io/learn/api-docs/ballerina/
Apache License 2.0
136 stars 64 forks source link

Introduce a Ballerina connector for Gemini REST API #7234

Open NipunaRanasinghe opened 1 month ago

NipunaRanasinghe commented 1 month ago

We are planning to introduce a new Ballerina connector for the latest Gemini REST API by generated using it's OpenAPI specification.

Related links:

This includes the following tasks:

  1. Create a new repository under 'ballerina-platform' organization.
  2. Create the OpenAPI specification manually or by converting it from other API descriptor formats.
  3. Generate the connector client.
  4. Implement a test suite covering the core functionalities.
  5. Write documentation with examples.
dilshanfardil commented 1 month ago

Hi @NipunaRanasinghe,

I would like to work on this project.

Thank You Dilshan F.

Nuvindu commented 1 month ago

👋 Welcome, @dilshanfardil ! 🚀

We're thrilled to have you join the Ballerina Hacktoberfest community! The issue has been assigned to you, and we’re excited to see your contributions.

To help you get started, here are a few essential resources:

  1. Connector Contributor Guide: Make sure to go through our Ballerina Hacktoberfest connector contributor guidelines and follow the exact steps, to contribute effectively.
  2. Learn Ballerina: Whether you're new to Ballerina or looking to enhance your skills, check out our official learning resources.
  3. Community & Support: If you have technical questions, feel free to ask on Stack Overflow with the Ballerina tag, or join us on Discord to connect with other community members.

No contribution is too small, and your feedback is always welcome! Don’t hesitate to ask questions, propose new ideas, or report issues.

We are currently in the process of creating a GitHub repository for this connector module and will update you once it’s available. In the meantime, please go through the relevant resources and documentation related to the connector.

Happy coding and welcome aboard! 🎉

dilshanfardil commented 1 month ago

Hi Team,

Gemini don’t use OpenAPI specs; they use protocol buffer specs that are converted to discovery documents. So in order to crate the Open API spec I have used the protocol buffer.

I used this gnostic in order to generate the Open API spec.

Thank You Dilshan F.

dilshanfardil commented 1 month ago

Hi Team,

When checking the Gemini protocol buffers we could see that there are few versions

In the official documents also they are mentioning on v1beta version and it contains lot of features including file uploads as well. But as per the api-versioning-doc the v1 beta version includes early-access features that may be under development and is subject to rapid and breaking changes and also there is no guarantee that the features in the Beta version will move to the stable version.

Due to this instability, I will use the stable version v1 in order to create the connector.

Thank You Dilshan F.

Nuvindu commented 1 month ago

Using the stable version would be the optimal choice here

dilshanfardil commented 1 month ago

Hi @NipunaRanasinghe, @xlight05 and @Nuvindu

After reviewing the exposed APIs in both v1 and v1beta, there are significant differences between them, with v1beta offering several useful features that are not available in v1, such as:

You can find the relevant functions here.

Given these differences, I explored the possibility of integrating the v1beta version into the connector. Following a discussion with @xlight05, we learned that Google introduces new features in v1beta, and once those features are stable, they are promoted to v1. However, it appears that stable APIs are not being promoted to a v2 version, as referenced here

Upon examining SDKs like JavaScript, Python, and Android, we noticed that they also incorporate v1beta features. In my view, certain functions from v1beta should be included in the connector.

Could we consider releasing both versions, structured as follows:

WDYT ? @NipunaRanasinghe @Nuvindu @xlight05

Thank You Dilshan F.

Nuvindu commented 1 month ago

Hi @dilshanfardil , IMO, releasing the stable version is sufficient for now. And we can do a release for the beta version later if additional features are requested by a user.

dilshanfardil commented 3 weeks ago

Hi @Nuvindu and Team,

Since I am using the stable version, there are two open api specs. In the repo I could see that the module name as module-ballerinax-googleapis.gemini. So how can we proceed.

Shall I merge this two spec to one or shall I create two repos as

Thank You Dilshan F.

Nuvindu commented 3 weeks ago

Hi @dilshanfardil,

I have briefly gone through these specs and I think we can proceed with merging them into a single spec because both specs use the same endpoint URL.

However, if these APIs are managed and released separately within the product, then it would be better to keep them as separate Ballerina packages. Could you check and confirm? (Eg: We have used separate modules for the DocuSign connector as it has separately managed APIs https://developers.docusign.com/docs/)

For now, my suggestion is that we can proceed with merging. @NipunaRanasinghe @xlight05 WDYT?

xlight05 commented 3 weeks ago

@Nuvindu , we had a offline chat with @NipunaRanasinghe what we discussed was this could have indepddeant releases, to add more, there's lots of modules with beta tag. its safer to go with separate packages rather than having it in one. currently the plan is to prioritize generative with the existing repo and then move to model. we'll have the generative one in the current repo. We can change the repo name later on.

Nuvindu commented 3 weeks ago

Agreed. +1

dilshanfardil commented 1 week ago

Hi @NipunaRanasinghe @xlight05 @Nuvindu

Upon reviewing the generated code from the .bal file, we noticed that it does not include any authorization details in the methods. Additionally, the proto files also lack a mechanism to pass the access token.

@display {label: "Connection Config"}
public type ConnectionConfig record {|
    # The HTTP version understood by the client
    http:HttpVersion httpVersion = http:HTTP_2_0;
    # Configurations related to HTTP/1.x protocol
    ClientHttp1Settings http1Settings?;
    # Configurations related to HTTP/2 protocol
    http:ClientHttp2Settings http2Settings?;
    # The maximum time to wait (in seconds) for a response before closing the connection
    decimal timeout = 60;
    # The choice of setting `forwarded`/`x-forwarded` header
    string forwarded = "disable";
    # Configurations associated with request pooling
    http:PoolConfiguration poolConfig?;
    # HTTP caching related configurations
    http:CacheConfig cache?;
    # Specifies the way of handling compression (`accept-encoding`) header
    http:Compression compression = http:COMPRESSION_AUTO;
    # Configurations associated with the behaviour of the Circuit Breaker
    http:CircuitBreakerConfig circuitBreaker?;
    # Configurations associated with retrying
    http:RetryConfig retryConfig?;
    # Configurations associated with inbound response size limits
    http:ResponseLimitConfigs responseLimits?;
    # SSL/TLS-related options
    http:ClientSecureSocket secureSocket?;
    # Proxy server related options
    http:ProxyConfig proxy?;
    # Enables the inbound payload validation functionality which provided by the constraint package. Enabled by default
    boolean validation = true;
|};

and, a sample method

remote isolated function GenerativeService_BatchEmbedContents(string model, BatchEmbedContentsRequest payload, map<string|string[]> headers = {}) returns BatchEmbedContentsResponse|error {
        string resourcePath = string `/v1/models/${getEncodedUri(model)}:batchEmbedContents`;
        http:Request request = new;
        json jsonBody = payload.toJson();
        request.setPayload(jsonBody, "application/json");
        return self.clientEp->post(resourcePath, request, headers);
}

We can pass the headers and there is no point that we can pass it as a query param. Without this being included in the OpenAPI specification, can we consider adding it manually?

If we proceed with this approach, there are multiple mechanisms to invoke the API. One commonly used method is passing the API key as query parameters; however, this doesn't seem very secure.

Is there a standard we should follow in such scenarios, or should we proceed with query parameters despite the concerns?

Looking forward to your thoughts.

xlight05 commented 1 week ago

Hi @NipunaRanasinghe @xlight05 @Nuvindu

Upon reviewing the generated code from the .bal file, we noticed that it does not include any authorization details in the methods. Additionally, the proto files also lack a mechanism to pass the access token.

@display {label: "Connection Config"}
public type ConnectionConfig record {|
    # The HTTP version understood by the client
    http:HttpVersion httpVersion = http:HTTP_2_0;
    # Configurations related to HTTP/1.x protocol
    ClientHttp1Settings http1Settings?;
    # Configurations related to HTTP/2 protocol
    http:ClientHttp2Settings http2Settings?;
    # The maximum time to wait (in seconds) for a response before closing the connection
    decimal timeout = 60;
    # The choice of setting `forwarded`/`x-forwarded` header
    string forwarded = "disable";
    # Configurations associated with request pooling
    http:PoolConfiguration poolConfig?;
    # HTTP caching related configurations
    http:CacheConfig cache?;
    # Specifies the way of handling compression (`accept-encoding`) header
    http:Compression compression = http:COMPRESSION_AUTO;
    # Configurations associated with the behaviour of the Circuit Breaker
    http:CircuitBreakerConfig circuitBreaker?;
    # Configurations associated with retrying
    http:RetryConfig retryConfig?;
    # Configurations associated with inbound response size limits
    http:ResponseLimitConfigs responseLimits?;
    # SSL/TLS-related options
    http:ClientSecureSocket secureSocket?;
    # Proxy server related options
    http:ProxyConfig proxy?;
    # Enables the inbound payload validation functionality which provided by the constraint package. Enabled by default
    boolean validation = true;
|};

and, a sample method

remote isolated function GenerativeService_BatchEmbedContents(string model, BatchEmbedContentsRequest payload, map<string|string[]> headers = {}) returns BatchEmbedContentsResponse|error {
        string resourcePath = string `/v1/models/${getEncodedUri(model)}:batchEmbedContents`;
        http:Request request = new;
        json jsonBody = payload.toJson();
        request.setPayload(jsonBody, "application/json");
        return self.clientEp->post(resourcePath, request, headers);
}

We can pass the headers and there is no point that we can pass it as a query param. Without this being included in the OpenAPI specification, can we consider adding it manually?

If we proceed with this approach, there are multiple mechanisms to invoke the API. One commonly used method is passing the API key as query parameters; however, this doesn't seem very secure.

Is there a standard we should follow in such scenarios, or should we proceed with query parameters despite the concerns?

Looking forward to your thoughts.

We usually rely on auth fields of openapi specs when generating connectors. The openapi tool automatically generates auth configs accordingly. We need to see why those fields are missing in openapi spec. We need to look at discovery APIs to clarify this. I had an internal discussion with library team. It seems like we haven't converted any discovery apis to openapi specs ourselves. One thing we can do is to explore the openapi spec used in google calendar package and its discovery API whether the discovery API contains the auth related details.

dilshanfardil commented 1 week ago

Thanks for the input, @xlight05 I'll review the current implementation of some sample APIs. There should be a way to pass the API key, so I'll explore the possibilities and come up with a plan.