Skip to content

Commit 09d342b

Browse files
committed
init genai messages
1 parent 5307e20 commit 09d342b

File tree

13 files changed

+818
-0
lines changed

13 files changed

+818
-0
lines changed

instrumentation-api-incubator/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ dependencies {
1616
api("io.opentelemetry:opentelemetry-api-incubator")
1717

1818
compileOnly("com.google.auto.value:auto-value-annotations")
19+
compileOnly("com.fasterxml.jackson.core:jackson-databind")
1920
annotationProcessor("com.google.auto.value:auto-value")
2021

2122
testImplementation(project(":testing-common"))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package io.opentelemetry.instrumentation.api.incubator.semconv.genai.messages;
2+
3+
import com.fasterxml.jackson.annotation.JsonClassDescription;
4+
import com.fasterxml.jackson.annotation.JsonProperty;
5+
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
6+
import com.google.auto.value.AutoValue;
7+
import java.util.Map;
8+
9+
/**
10+
* Represents an arbitrary message part with any type and properties.
11+
* This allows for extensibility with custom message part types.
12+
*/
13+
@AutoValue
14+
@JsonClassDescription("Generic part")
15+
public abstract class GenericPart implements MessagePart {
16+
17+
@JsonProperty(required = true, value = "type")
18+
@JsonPropertyDescription("The type of the content captured in this part")
19+
public abstract String getType();
20+
21+
public static GenericPart create(String type) {
22+
return new AutoValue_GenericPart(type);
23+
}
24+
25+
public static GenericPart create(String type, Map<String, Object> additionalProperties) {
26+
return new AutoValue_GenericPart(type);
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package io.opentelemetry.instrumentation.api.incubator.semconv.genai.messages;
2+
3+
import com.fasterxml.jackson.annotation.JsonClassDescription;
4+
import com.fasterxml.jackson.annotation.JsonProperty;
5+
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
6+
import com.google.auto.value.AutoValue;
7+
import java.util.List;
8+
9+
/**
10+
* Represents an input message sent to the model.
11+
*/
12+
@AutoValue
13+
@JsonClassDescription("Input message")
14+
public abstract class InputMessage {
15+
16+
@JsonProperty(required = true, value = "role")
17+
@JsonPropertyDescription("Role of the entity that created the message")
18+
public abstract String getRole();
19+
20+
@JsonProperty(required = true, value = "parts")
21+
@JsonPropertyDescription("List of message parts that make up the message content")
22+
public abstract List<MessagePart> getParts();
23+
24+
public static InputMessage create(String role, List<MessagePart> parts) {
25+
return new AutoValue_InputMessage(role, parts);
26+
}
27+
28+
public static InputMessage create(Role role, List<MessagePart> parts) {
29+
return new AutoValue_InputMessage(role.getValue(), parts);
30+
}
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package io.opentelemetry.instrumentation.api.incubator.semconv.genai.messages;
2+
3+
import com.fasterxml.jackson.core.JsonProcessingException;
4+
import com.fasterxml.jackson.databind.ObjectMapper;
5+
import com.google.auto.value.AutoValue;
6+
import java.util.ArrayList;
7+
import java.util.List;
8+
9+
/**
10+
* Represents a collection of input messages sent to the model.
11+
*/
12+
@AutoValue
13+
public abstract class InputMessages {
14+
15+
public abstract List<InputMessage> getMessages();
16+
17+
public static InputMessages create() {
18+
return new AutoValue_InputMessages(new ArrayList<>());
19+
}
20+
21+
public static InputMessages create(List<InputMessage> messages) {
22+
return new AutoValue_InputMessages(new ArrayList<>(messages));
23+
}
24+
25+
public InputMessages append(InputMessage inputMessage) {
26+
List<InputMessage> messages = getMessages();
27+
messages.add(inputMessage);
28+
return this;
29+
}
30+
31+
public String toJsonString() {
32+
try {
33+
ObjectMapper mapper = new ObjectMapper();
34+
return mapper.writeValueAsString(this);
35+
} catch (JsonProcessingException e) {
36+
throw new RuntimeException("Failed to serialize InputMessages to JSON", e);
37+
}
38+
}
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package io.opentelemetry.instrumentation.api.incubator.semconv.genai.messages;
2+
3+
/**
4+
* Interface for all message parts.
5+
*/
6+
public interface MessagePart {
7+
8+
/**
9+
* Get the type of this message part.
10+
* @return the type string
11+
*/
12+
String getType();
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package io.opentelemetry.instrumentation.api.incubator.semconv.genai.messages;
2+
3+
import com.fasterxml.jackson.annotation.JsonClassDescription;
4+
import com.fasterxml.jackson.annotation.JsonProperty;
5+
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
6+
import com.google.auto.value.AutoValue;
7+
import java.util.List;
8+
9+
@AutoValue
10+
@JsonClassDescription("Output message")
11+
public abstract class OutputMessage {
12+
13+
@JsonProperty(required = true, value = "role")
14+
@JsonPropertyDescription("Role of response")
15+
public abstract String getRole();
16+
17+
@JsonProperty(required = true, value = "parts")
18+
@JsonPropertyDescription("List of message parts that make up the message content")
19+
public abstract List<MessagePart> getParts();
20+
21+
@JsonProperty(required = true, value = "finish_reason")
22+
@JsonPropertyDescription("Reason for finishing the generation")
23+
public abstract String getFinishReason();
24+
25+
public static OutputMessage create(String role, List<MessagePart> parts, String finishReason) {
26+
return new AutoValue_OutputMessage(role, parts, finishReason);
27+
}
28+
29+
public static OutputMessage create(Role role, List<MessagePart> parts, String finishReason) {
30+
return new AutoValue_OutputMessage(role.getValue(), parts, finishReason);
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package io.opentelemetry.instrumentation.api.incubator.semconv.genai.messages;
2+
3+
import com.fasterxml.jackson.core.JsonProcessingException;
4+
import com.fasterxml.jackson.databind.ObjectMapper;
5+
import com.google.auto.value.AutoValue;
6+
import java.util.ArrayList;
7+
import java.util.List;
8+
9+
/**
10+
* Represents a collection of output messages from the model.
11+
*/
12+
@AutoValue
13+
public abstract class OutputMessages {
14+
15+
public abstract List<OutputMessage> getMessages();
16+
17+
public static OutputMessages create() {
18+
return new AutoValue_OutputMessages(new ArrayList<>());
19+
}
20+
21+
public static OutputMessages create(List<OutputMessage> messages) {
22+
return new AutoValue_OutputMessages(new ArrayList<>(messages));
23+
}
24+
25+
public OutputMessages append(OutputMessage outputMessage) {
26+
List<OutputMessage> currentMessages = new ArrayList<>(getMessages());
27+
currentMessages.add(outputMessage);
28+
return new AutoValue_OutputMessages(currentMessages);
29+
}
30+
31+
public String toJsonString() {
32+
try {
33+
ObjectMapper mapper = new ObjectMapper();
34+
return mapper.writeValueAsString(this);
35+
} catch (JsonProcessingException e) {
36+
throw new RuntimeException("Failed to serialize OutputMessages to JSON", e);
37+
}
38+
}
39+
40+
/**
41+
* Merges a chunk OutputMessage into the existing messages at the specified index.
42+
* This method is used for streaming responses where content is received in chunks.
43+
*
44+
* @param index the index of the message to merge into
45+
* @param chunkMessage the chunk message to append
46+
* @return a new OutputMessages instance with the merged content
47+
*/
48+
public OutputMessages merge(int index, OutputMessage chunkMessage) {
49+
List<OutputMessage> currentMessages = new ArrayList<>(getMessages());
50+
51+
if (index < 0 || index >= currentMessages.size()) {
52+
throw new IllegalArgumentException("Index " + index + " is out of bounds for messages list of size " + currentMessages.size());
53+
}
54+
55+
OutputMessage existingMessage = currentMessages.get(index);
56+
57+
// Merge the parts by appending text content from chunk to existing message
58+
List<MessagePart> mergedParts = new ArrayList<>(existingMessage.getParts());
59+
60+
// If the chunk message has text parts, append their content to the first text part of existing message
61+
for (MessagePart chunkPart : chunkMessage.getParts()) {
62+
if (chunkPart instanceof TextPart) {
63+
TextPart chunkTextPart = (TextPart) chunkPart;
64+
65+
// Find the first text part in existing message to append to
66+
boolean appended = false;
67+
for (int i = 0; i < mergedParts.size(); i++) {
68+
MessagePart existingPart = mergedParts.get(i);
69+
if (existingPart instanceof TextPart) {
70+
TextPart existingTextPart = (TextPart) existingPart;
71+
// Create a new TextPart with combined content
72+
TextPart mergedTextPart = TextPart.create(existingTextPart.getContent() + chunkTextPart.getContent());
73+
mergedParts.set(i, mergedTextPart);
74+
appended = true;
75+
break;
76+
}
77+
}
78+
79+
// If no existing text part found, add the chunk as a new part
80+
if (!appended) {
81+
mergedParts.add(chunkTextPart);
82+
}
83+
} else {
84+
// For non-text parts, add them as new parts
85+
mergedParts.add(chunkPart);
86+
}
87+
}
88+
89+
// Create new OutputMessage with merged parts, using the chunk's finish reason if available
90+
String finalFinishReason = chunkMessage.getFinishReason() != null ?
91+
chunkMessage.getFinishReason() : existingMessage.getFinishReason();
92+
93+
OutputMessage mergedMessage = OutputMessage.create(
94+
existingMessage.getRole(),
95+
mergedParts,
96+
finalFinishReason
97+
);
98+
99+
// Replace the message at the specified index
100+
currentMessages.set(index, mergedMessage);
101+
102+
return new AutoValue_OutputMessages(currentMessages);
103+
}
104+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package io.opentelemetry.instrumentation.api.incubator.semconv.genai.messages;
2+
3+
public enum Role {
4+
SYSTEM("system"),
5+
USER("user"),
6+
ASSISTANT("assistant"),
7+
TOOL("tool"),
8+
DEVELOPER("developer");
9+
10+
private final String value;
11+
12+
public String getValue() {
13+
return value;
14+
}
15+
16+
Role(String value) {
17+
this.value = value;
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package io.opentelemetry.instrumentation.api.incubator.semconv.genai.messages;
2+
3+
import com.fasterxml.jackson.annotation.JsonClassDescription;
4+
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
5+
import com.fasterxml.jackson.core.JsonProcessingException;
6+
import com.fasterxml.jackson.databind.ObjectMapper;
7+
import com.google.auto.value.AutoValue;
8+
import java.util.List;
9+
10+
/**
11+
* Represents the list of system instructions sent to the model.
12+
*/
13+
@AutoValue
14+
@JsonClassDescription("System instructions")
15+
public abstract class SystemInstructions {
16+
17+
@JsonPropertyDescription("List of message parts that make up the system instructions")
18+
public abstract List<MessagePart> getParts();
19+
20+
public static SystemInstructions create(List<MessagePart> parts) {
21+
return new AutoValue_SystemInstructions(parts);
22+
}
23+
24+
public String toJsonString() {
25+
try {
26+
ObjectMapper mapper = new ObjectMapper();
27+
return mapper.writeValueAsString(this);
28+
} catch (JsonProcessingException e) {
29+
throw new RuntimeException("Failed to serialize SystemInstructions to JSON", e);
30+
}
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package io.opentelemetry.instrumentation.api.incubator.semconv.genai.messages;
2+
3+
import com.fasterxml.jackson.annotation.JsonClassDescription;
4+
import com.fasterxml.jackson.annotation.JsonProperty;
5+
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
6+
import com.google.auto.value.AutoValue;
7+
8+
/**
9+
* Represents text content sent to or received from the model.
10+
*/
11+
@AutoValue
12+
@JsonClassDescription("Text part")
13+
public abstract class TextPart implements MessagePart {
14+
15+
@JsonProperty(required = true, value = "type")
16+
@JsonPropertyDescription("The type of the content captured in this part")
17+
public abstract String getType();
18+
19+
@JsonProperty(required = true, value = "content")
20+
@JsonPropertyDescription("Text content sent to or received from the model")
21+
public abstract String getContent();
22+
23+
public static TextPart create(String content) {
24+
return new AutoValue_TextPart("text", content);
25+
}
26+
}

0 commit comments

Comments
 (0)