Skip to content

Commit 0a8ad3f

Browse files
chore: Convert JSON LDValue into the AiConfig Object
1 parent 5365cd9 commit 0a8ad3f

File tree

7 files changed

+383
-5
lines changed

7 files changed

+383
-5
lines changed
Lines changed: 157 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,27 @@
11
package com.launchdarkly.sdk.server.ai;
22

3+
import java.util.ArrayList;
4+
import java.util.HashMap;
5+
import java.util.List;
6+
import java.util.Optional;
7+
38
import com.launchdarkly.logging.LDLogger;
49
import com.launchdarkly.sdk.server.interfaces.LDClientInterface;
10+
import com.launchdarkly.sdk.LDValue;
11+
import com.launchdarkly.sdk.LDValueType;
12+
import com.launchdarkly.sdk.server.ai.datamodel.AiConfig;
13+
import com.launchdarkly.sdk.server.ai.datamodel.Message;
14+
import com.launchdarkly.sdk.server.ai.datamodel.Meta;
15+
import com.launchdarkly.sdk.server.ai.datamodel.Model;
16+
import com.launchdarkly.sdk.server.ai.datamodel.Provider;
17+
import com.launchdarkly.sdk.server.ai.datamodel.Role;
518
import com.launchdarkly.sdk.server.ai.interfaces.LDAiClientInterface;
619

720
/**
8-
* The LaunchDarkly AI client. The client is capable of retrieving AI Configs from LaunchDarkly,
9-
* and generating events specific to usage of the AI Config when interacting with model providers.
21+
* The LaunchDarkly AI client. The client is capable of retrieving AI Configs
22+
* from LaunchDarkly,
23+
* and generating events specific to usage of the AI Config when interacting
24+
* with model providers.
1025
*/
1126
public class LDAiClient implements LDAiClientInterface {
1227
private LDClientInterface client;
@@ -15,14 +30,151 @@ public class LDAiClient implements LDAiClientInterface {
1530
/**
1631
* Creates a {@link LDAiClient}
1732
*
18-
* @param client LaunchDarkly Java Server SDK
33+
* @param client LaunchDarkly Java Server SDK
1934
*/
2035
public LDAiClient(LDClientInterface client) {
21-
if(client == null) {
22-
//Error
36+
if (client == null) {
37+
// Error
2338
} else {
2439
this.client = client;
2540
this.logger = client.getLogger();
2641
}
2742
}
43+
44+
/**
45+
* Method to convert the JSON variable into the AiConfig object
46+
*
47+
* Currently, I am doing this in a mutable way, just to verify the logic convert
48+
* LDValue into AiConfig.
49+
* It is possible we need a builder to create immutable version of this for ease
50+
* of use in a later PR.
51+
*
52+
* @param value
53+
* @param key
54+
*/
55+
protected AiConfig parseAiConfig(LDValue value, String key) {
56+
AiConfig result = new AiConfig();
57+
58+
try {
59+
// Verify the whole value is a JSON object
60+
if (value == null || value.getType() != LDValueType.OBJECT) {
61+
throw new AiConfigParseException("Input to parseAiConfig must be a JSON object");
62+
}
63+
64+
// Convert the _meta JSON object into Meta
65+
LDValue valueMeta = value.get("_ldMeta");
66+
if (valueMeta == LDValue.ofNull() || valueMeta.getType() != LDValueType.OBJECT) {
67+
throw new AiConfigParseException("_ldMeta must be a JSON object");
68+
}
69+
70+
Meta meta = new Meta();
71+
// TODO: Do we expect customer calling this to build default value?
72+
// If we do, then some of the values would be null
73+
meta.setEnabled(ldValueNullCheck(valueMeta.get("enabled")).booleanValue());
74+
meta.setVariationKey(ldValueNullCheck(valueMeta.get("variationKey")).stringValue());
75+
Optional<Integer> version = Optional.of(valueMeta.get("version").intValue());
76+
meta.setVersion(version);
77+
result.setMeta(meta);
78+
79+
// Convert the optional messages from an JSON array of JSON objects into Message
80+
// Q: Does it even make sense to have 0 messages?
81+
LDValue valueMessages = value.get("messages");
82+
if (valueMeta == LDValue.ofNull() || valueMessages.getType() != LDValueType.ARRAY) {
83+
throw new AiConfigParseException("messages must be a JSON array");
84+
}
85+
86+
List<Message> messages = new ArrayList<Message>();
87+
for (LDValue valueMessage : valueMessages.values()) {
88+
if (valueMessage == LDValue.ofNull() || valueMessage.getType() != LDValueType.OBJECT) {
89+
throw new AiConfigParseException("individual message must be a JSON object");
90+
}
91+
92+
Message message = new Message();
93+
message.setContent(ldValueNullCheck(valueMessage.get("content")).stringValue());
94+
// TODO: For absolute safety, we need to check this is one out of the three
95+
// possible enum
96+
message.setRole(Role.valueOf(valueMessage.get("role").stringValue().toUpperCase()));
97+
messages.add(message);
98+
}
99+
result.setMessages(messages);
100+
101+
// Convert the optional model from an JSON object of with parameters and custom
102+
// into Model
103+
LDValue valueModel = value.get("model");
104+
if (valueModel == LDValue.ofNull() || valueModel.getType() != LDValueType.OBJECT) {
105+
throw new AiConfigParseException("model must be a JSON object");
106+
}
107+
108+
Model model = new Model();
109+
model.setName(ldValueNullCheck(valueModel.get("name")).stringValue());
110+
111+
LDValue valueParameters = valueModel.get("parameters");
112+
if (valueParameters.getType() != LDValueType.NULL) {
113+
if (valueParameters.getType() != LDValueType.OBJECT) {
114+
throw new AiConfigParseException("non-null parameters must be a JSON object");
115+
}
116+
117+
HashMap<String, LDValue> parameters = new HashMap<>();
118+
for (String k : valueParameters.keys()) {
119+
parameters.put(k, valueParameters.get(k));
120+
}
121+
model.setParameters(parameters);
122+
} else {
123+
// Parameters is optional - so we can just set null and proceed
124+
125+
// TODO: Mustash validation somewhere
126+
model.setParameters(null);
127+
}
128+
129+
LDValue valueCustom = valueModel.get("custom");
130+
if (valueCustom.getType() != LDValueType.NULL) {
131+
if (valueCustom.getType() != LDValueType.OBJECT) {
132+
throw new AiConfigParseException("non-null custom must be a JSON object");
133+
}
134+
135+
HashMap<String, LDValue> custom = new HashMap<>();
136+
for (String k : valueCustom.keys()) {
137+
custom.put(k, valueCustom.get(k));
138+
}
139+
model.setCustom(custom);
140+
} else {
141+
// Custom is optional - we can just set null and proceed
142+
model.setCustom(null);
143+
}
144+
result.setModel(model);
145+
146+
// Convert the optional provider from an JSON object of with name into Provider
147+
LDValue valueProvider = value.get("provider");
148+
if (valueProvider.getType() != LDValueType.NULL) {
149+
if (valueProvider.getType() != LDValueType.OBJECT) {
150+
throw new AiConfigParseException("non-null provider must be a JSON object");
151+
}
152+
153+
Provider provider = new Provider();
154+
provider.setName(ldValueNullCheck(valueProvider.get("name")).stringValue());
155+
result.setProvider(provider);
156+
} else {
157+
// Provider is optional - we can just set null and proceed
158+
result.setProvider(null);
159+
}
160+
} catch (AiConfigParseException e) {
161+
// logger.error(e.getMessage());
162+
return null;
163+
}
164+
165+
return result;
166+
}
167+
168+
protected <T> T ldValueNullCheck(T ldValue) throws AiConfigParseException {
169+
if (ldValue == LDValue.ofNull()) {
170+
throw new AiConfigParseException("Unexpected Null value for non-optional field");
171+
}
172+
return ldValue;
173+
}
174+
175+
class AiConfigParseException extends Exception {
176+
AiConfigParseException(String exceptionMessage) {
177+
super(exceptionMessage);
178+
}
179+
}
28180
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package com.launchdarkly.sdk.server.ai.datamodel;
2+
3+
import java.util.List;
4+
5+
public class AiConfig {
6+
private List<Message> messages;
7+
8+
private Meta meta;
9+
10+
private Model model;
11+
12+
private Provider provider;
13+
14+
public List<Message> getMessages() {
15+
return messages;
16+
}
17+
18+
public void setMessages(List<Message> messages) {
19+
this.messages = messages;
20+
}
21+
22+
public Meta getMeta() {
23+
return meta;
24+
}
25+
26+
public void setMeta(Meta meta) {
27+
this.meta = meta;
28+
}
29+
30+
public Model getModel() {
31+
return model;
32+
}
33+
34+
public void setModel(Model model) {
35+
this.model = model;
36+
}
37+
38+
public Provider getProvider() {
39+
return provider;
40+
}
41+
42+
public void setProvider(Provider provider) {
43+
this.provider = provider;
44+
}
45+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.launchdarkly.sdk.server.ai.datamodel;
2+
3+
public class Message {
4+
private String content;
5+
6+
private Role role;
7+
8+
public String getContent() {
9+
return content;
10+
}
11+
12+
public void setContent(String content) {
13+
this.content = content;
14+
}
15+
16+
public Role getRole() {
17+
return role;
18+
}
19+
20+
public void setRole(Role role) {
21+
this.role = role;
22+
}
23+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.launchdarkly.sdk.server.ai.datamodel;
2+
3+
import java.util.Optional;
4+
5+
public class Meta {
6+
/**
7+
* The variation key.
8+
*/
9+
private String variationKey;
10+
11+
/**
12+
* The variation version.
13+
*/
14+
private Optional<Integer> version;
15+
16+
/**
17+
* If the config is enabled.
18+
*/
19+
private boolean enabled;
20+
21+
// Getters and Setters
22+
23+
public String getVariationKey() {
24+
return variationKey;
25+
}
26+
27+
public void setVariationKey(String variationKey) {
28+
this.variationKey = variationKey;
29+
}
30+
31+
public Optional<Integer> getVersion() {
32+
return version;
33+
}
34+
35+
public void setVersion(Optional<Integer> version) {
36+
this.version = version;
37+
}
38+
39+
public boolean isEnabled() {
40+
return enabled;
41+
}
42+
43+
public void setEnabled(boolean enabled) {
44+
this.enabled = enabled;
45+
}
46+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.launchdarkly.sdk.server.ai.datamodel;
2+
3+
import java.util.HashMap;
4+
5+
import com.launchdarkly.sdk.LDValue;
6+
7+
public class Model {
8+
private String name;
9+
10+
private HashMap<String, LDValue> parameters;
11+
12+
private HashMap<String, LDValue> custom;
13+
14+
// Getters and Setters
15+
16+
public String getName() {
17+
return name;
18+
}
19+
20+
public void setName(String name) {
21+
this.name = name;
22+
}
23+
24+
public HashMap<String, LDValue> getParameters() {
25+
return parameters;
26+
}
27+
28+
public void setParameters(HashMap<String, LDValue> parameters) {
29+
this.parameters = parameters;
30+
}
31+
32+
public HashMap<String, LDValue> getCustom() {
33+
return custom;
34+
}
35+
36+
public void setCustom(HashMap<String, LDValue> custom) {
37+
this.custom = custom;
38+
}
39+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.launchdarkly.sdk.server.ai.datamodel;
2+
3+
public class Provider {
4+
private String name;
5+
6+
public String getName() {
7+
return name;
8+
}
9+
10+
public void setName(String name) {
11+
this.name = name;
12+
}
13+
}

0 commit comments

Comments
 (0)