Thanks to visit codestin.com
Credit goes to github.com

Skip to content

chore: Convert JSON LDValue into the AiConfig Object #61

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

Open
wants to merge 1 commit into
base: lc/SDK-1192/AI-config-feature-branch
Choose a base branch
from

Conversation

louis-launchdarkly
Copy link

For ease of discussion, implement only the conversion logic from LDValue (that we will get back from the Feature Flag SDK) to the AiConfig DataModel.

This uses the exception-based short circuiting (similar to https://github.com/launchdarkly/dotnet-core/blob/6774539af1a2f87b96ae3e647fdcdc5663c791ab/pkgs/sdk/server-ai/src/LdAiClient.cs#L180) and LDValue reading logic similar to https://github.com/launchdarkly/python-server-sdk-ai/blob/main/ldai/client.py#L134

@louis-launchdarkly louis-launchdarkly requested a review from a team as a code owner May 8, 2025 22:15
Base automatically changed from lc/SDK-1192/setup-new-project to lc/SDK-1192/AI-config-feature-branch May 8, 2025 22:16
@louis-launchdarkly
Copy link
Author

I just realized I also need to copy the .github file from another project in this mono-repo to have the test run in CI. I am running the test locally via ./gradlew build right now.


import java.util.List;

public class AiConfig {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All concrete public classes should be final.

@@ -15,14 +30,151 @@ public class LDAiClient implements LDAiClientInterface {
/**
* Creates a {@link LDAiClient}
*
* @param client LaunchDarkly Java Server SDK
* @param client LaunchDarkly Java Server SDK
*/
public LDAiClient(LDClientInterface client) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment on AI config. Should be final.

*
* Currently, I am doing this in a mutable way, just to verify the logic convert
* LDValue into AiConfig.
* It is possible we need a builder to create immutable version of this for ease
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, AI config should be immutable with a builder. Because the customer will need to make default values and we will need them to be immutable. Imagine we are writing the value into the default value for an event while the customer is mutating the AI config.


// Convert the _meta JSON object into Meta
LDValue valueMeta = value.get("_ldMeta");
if (valueMeta == LDValue.ofNull() || valueMeta.getType() != LDValueType.OBJECT) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type of a null LDValue is LDValueType null. So, valueMeta.getType() == LDValueType.OBJECT, then you know it is an object and not null. You don't need the extra checks.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would probably re-arrange things into type guards, logging when there is a problem, but never really throwing.

if(check(logger, value, LDValueType.ARRAY, "message must be a JSON array")) { // it is an array }

Then do things with the array when it is, and something gets logged when it isn't. Or something similar.

}

List<Message> messages = new ArrayList<Message>();
for (LDValue valueMessage : valueMessages.values()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LDValue provides a valuesAs method, which turns allows you to convert an array functional style using a converter functor.

Copy link
Member

@kinyoklion kinyoklion May 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

    LDValue valueMessages = LDValue.arrayOf(
            LDValue.buildObject().put("content", "the user content").put("role", "user").build(),
            LDValue.buildObject().put("content", "the  system content").put("role", "system").build()
    );

    Iterable<Message> messages = valueMessages.valuesAs(new Message.MessageConverter());

Assuming this message implementation:

import com.launchdarkly.sdk.LDValue;

public class Message {

    static class MessageConverter extends LDValue.Converter<Message> {
        @Override
        public LDValue fromType(Message message) {
            return LDValue.buildObject()
                    .put("content", message.getContent())
                    .put("role", message.getRole().toString())
                    .build();
        }

        @Override
        public Message toType(LDValue ldValue) {
            return new Message(ldValue.get("content").stringValue(), Role.getRole(ldValue.get("role").stringValue()));
        }
    }

    Message(String content, Role role) {
        this.content = content;
        this.role = role;
    }

    private String content;

    private Role role;

    public String getContent() {
        return content;
    }

    public Role getRole() {
        return role;
    }
}

And this role implementation:

public enum Role {
    USER("user"),
    SYSTEM("system"),
    ASSISTANT("assistant");

    private final String role;
    private Role(String role) {
        this.role = role;
    }
    public static Role getRole(String role) {
        switch (role) {
            case "user":
                return USER;
            case "system":
                return SYSTEM;
            case "assistant":
                return ASSISTANT;
            default:
                return null;
        }
    }

    @Override
    public String toString() {
        return role;
    }
}

You could, of course, add some validation as well, but this is the structure that makes this composable .

import com.launchdarkly.sdk.server.ai.datamodel.Meta;
import com.launchdarkly.sdk.server.ai.datamodel.Model;
import com.launchdarkly.sdk.server.ai.datamodel.Provider;
import com.launchdarkly.sdk.server.ai.datamodel.Role;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is role missing from the PR?

Copy link
Member

@kinyoklion kinyoklion left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestions in comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants