Skip to content

Commit 9a24a05

Browse files
feat: add CodeArtifact type for code governance support (#11)
- Add CodeArtifact class for LLM-generated code detection metadata - Update PolicyInfo to include optional codeArtifact field - Update tests to use new constructor signature - Bump version to 1.3.0
1 parent efd4807 commit 9a24a05

4 files changed

Lines changed: 233 additions & 18 deletions

File tree

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>com.getaxonflow</groupId>
88
<artifactId>axonflow-sdk</artifactId>
9-
<version>1.2.0</version>
9+
<version>1.3.0</version>
1010
<packaging>jar</packaging>
1111

1212
<name>AxonFlow Java SDK</name>
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
/*
2+
* Copyright 2025 AxonFlow
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.getaxonflow.sdk.types;
17+
18+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
19+
import com.fasterxml.jackson.annotation.JsonProperty;
20+
21+
import java.util.Collections;
22+
import java.util.List;
23+
import java.util.Objects;
24+
25+
/**
26+
* Represents metadata for LLM-generated code detection.
27+
*
28+
* <p>When an LLM generates code in its response, AxonFlow automatically detects
29+
* and analyzes it. This metadata is included in PolicyInfo for audit and compliance.
30+
*/
31+
@JsonIgnoreProperties(ignoreUnknown = true)
32+
public final class CodeArtifact {
33+
34+
@JsonProperty("is_code_output")
35+
private final boolean isCodeOutput;
36+
37+
@JsonProperty("language")
38+
private final String language;
39+
40+
@JsonProperty("code_type")
41+
private final String codeType;
42+
43+
@JsonProperty("size_bytes")
44+
private final int sizeBytes;
45+
46+
@JsonProperty("line_count")
47+
private final int lineCount;
48+
49+
@JsonProperty("secrets_detected")
50+
private final int secretsDetected;
51+
52+
@JsonProperty("unsafe_patterns")
53+
private final int unsafePatterns;
54+
55+
@JsonProperty("policies_checked")
56+
private final List<String> policiesChecked;
57+
58+
/**
59+
* Creates a new CodeArtifact instance.
60+
*
61+
* @param isCodeOutput whether the response contains code
62+
* @param language detected programming language
63+
* @param codeType code category (function, class, script, etc.)
64+
* @param sizeBytes size of detected code in bytes
65+
* @param lineCount number of lines of code
66+
* @param secretsDetected count of potential secrets found
67+
* @param unsafePatterns count of unsafe code patterns
68+
* @param policiesChecked list of code governance policies evaluated
69+
*/
70+
public CodeArtifact(
71+
@JsonProperty("is_code_output") boolean isCodeOutput,
72+
@JsonProperty("language") String language,
73+
@JsonProperty("code_type") String codeType,
74+
@JsonProperty("size_bytes") int sizeBytes,
75+
@JsonProperty("line_count") int lineCount,
76+
@JsonProperty("secrets_detected") int secretsDetected,
77+
@JsonProperty("unsafe_patterns") int unsafePatterns,
78+
@JsonProperty("policies_checked") List<String> policiesChecked) {
79+
this.isCodeOutput = isCodeOutput;
80+
this.language = language != null ? language : "";
81+
this.codeType = codeType != null ? codeType : "";
82+
this.sizeBytes = sizeBytes;
83+
this.lineCount = lineCount;
84+
this.secretsDetected = secretsDetected;
85+
this.unsafePatterns = unsafePatterns;
86+
this.policiesChecked = policiesChecked != null ? Collections.unmodifiableList(policiesChecked) : Collections.emptyList();
87+
}
88+
89+
/**
90+
* Returns whether the response contains code.
91+
*
92+
* @return true if code was detected, false otherwise
93+
*/
94+
public boolean isCodeOutput() {
95+
return isCodeOutput;
96+
}
97+
98+
/**
99+
* Returns the detected programming language.
100+
*
101+
* @return the programming language (e.g., "python", "javascript", "go")
102+
*/
103+
public String getLanguage() {
104+
return language;
105+
}
106+
107+
/**
108+
* Returns the code category.
109+
*
110+
* @return the code type (e.g., "function", "class", "script", "config", "snippet")
111+
*/
112+
public String getCodeType() {
113+
return codeType;
114+
}
115+
116+
/**
117+
* Returns the size of detected code in bytes.
118+
*
119+
* @return code size in bytes
120+
*/
121+
public int getSizeBytes() {
122+
return sizeBytes;
123+
}
124+
125+
/**
126+
* Returns the number of lines of code.
127+
*
128+
* @return line count
129+
*/
130+
public int getLineCount() {
131+
return lineCount;
132+
}
133+
134+
/**
135+
* Returns the count of potential secrets found.
136+
*
137+
* @return number of secrets detected
138+
*/
139+
public int getSecretsDetected() {
140+
return secretsDetected;
141+
}
142+
143+
/**
144+
* Returns the count of unsafe code patterns.
145+
*
146+
* @return number of unsafe patterns detected
147+
*/
148+
public int getUnsafePatterns() {
149+
return unsafePatterns;
150+
}
151+
152+
/**
153+
* Returns the list of code governance policies that were evaluated.
154+
*
155+
* @return immutable list of policy names
156+
*/
157+
public List<String> getPoliciesChecked() {
158+
return policiesChecked;
159+
}
160+
161+
@Override
162+
public boolean equals(Object o) {
163+
if (this == o) return true;
164+
if (o == null || getClass() != o.getClass()) return false;
165+
CodeArtifact that = (CodeArtifact) o;
166+
return isCodeOutput == that.isCodeOutput &&
167+
sizeBytes == that.sizeBytes &&
168+
lineCount == that.lineCount &&
169+
secretsDetected == that.secretsDetected &&
170+
unsafePatterns == that.unsafePatterns &&
171+
Objects.equals(language, that.language) &&
172+
Objects.equals(codeType, that.codeType) &&
173+
Objects.equals(policiesChecked, that.policiesChecked);
174+
}
175+
176+
@Override
177+
public int hashCode() {
178+
return Objects.hash(isCodeOutput, language, codeType, sizeBytes, lineCount, secretsDetected, unsafePatterns, policiesChecked);
179+
}
180+
181+
@Override
182+
public String toString() {
183+
return "CodeArtifact{" +
184+
"isCodeOutput=" + isCodeOutput +
185+
", language='" + language + '\'' +
186+
", codeType='" + codeType + '\'' +
187+
", sizeBytes=" + sizeBytes +
188+
", lineCount=" + lineCount +
189+
", secretsDetected=" + secretsDetected +
190+
", unsafePatterns=" + unsafePatterns +
191+
", policiesChecked=" + policiesChecked +
192+
'}';
193+
}
194+
}

src/main/java/com/getaxonflow/sdk/types/PolicyInfo.java

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,22 @@ public final class PolicyInfo {
4444
@JsonProperty("risk_score")
4545
private final Double riskScore;
4646

47+
@JsonProperty("code_artifact")
48+
private final CodeArtifact codeArtifact;
49+
4750
public PolicyInfo(
4851
@JsonProperty("policies_evaluated") List<String> policiesEvaluated,
4952
@JsonProperty("static_checks") List<String> staticChecks,
5053
@JsonProperty("processing_time") String processingTime,
5154
@JsonProperty("tenant_id") String tenantId,
52-
@JsonProperty("risk_score") Double riskScore) {
55+
@JsonProperty("risk_score") Double riskScore,
56+
@JsonProperty("code_artifact") CodeArtifact codeArtifact) {
5357
this.policiesEvaluated = policiesEvaluated != null ? Collections.unmodifiableList(policiesEvaluated) : Collections.emptyList();
5458
this.staticChecks = staticChecks != null ? Collections.unmodifiableList(staticChecks) : Collections.emptyList();
5559
this.processingTime = processingTime;
5660
this.tenantId = tenantId;
5761
this.riskScore = riskScore;
62+
this.codeArtifact = codeArtifact;
5863
}
5964

6065
/**
@@ -137,6 +142,15 @@ public Double getRiskScore() {
137142
return riskScore;
138143
}
139144

145+
/**
146+
* Returns the code artifact metadata if code was detected in the response.
147+
*
148+
* @return the code artifact, or null if no code was detected
149+
*/
150+
public CodeArtifact getCodeArtifact() {
151+
return codeArtifact;
152+
}
153+
140154
@Override
141155
public boolean equals(Object o) {
142156
if (this == o) return true;
@@ -146,12 +160,13 @@ public boolean equals(Object o) {
146160
Objects.equals(staticChecks, that.staticChecks) &&
147161
Objects.equals(processingTime, that.processingTime) &&
148162
Objects.equals(tenantId, that.tenantId) &&
149-
Objects.equals(riskScore, that.riskScore);
163+
Objects.equals(riskScore, that.riskScore) &&
164+
Objects.equals(codeArtifact, that.codeArtifact);
150165
}
151166

152167
@Override
153168
public int hashCode() {
154-
return Objects.hash(policiesEvaluated, staticChecks, processingTime, tenantId, riskScore);
169+
return Objects.hash(policiesEvaluated, staticChecks, processingTime, tenantId, riskScore, codeArtifact);
155170
}
156171

157172
@Override
@@ -162,6 +177,7 @@ public String toString() {
162177
", processingTime='" + processingTime + '\'' +
163178
", tenantId='" + tenantId + '\'' +
164179
", riskScore=" + riskScore +
180+
", codeArtifact=" + codeArtifact +
165181
'}';
166182
}
167183
}

src/test/java/com/getaxonflow/sdk/types/AdditionalTypesTest.java

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ void testPolicyInfoCreation() {
3333
List.of("static-check-1"),
3434
"17.48ms",
3535
"tenant-123",
36-
0.75
36+
0.75,
37+
null
3738
);
3839

3940
assertThat(info.getPoliciesEvaluated()).containsExactly("policy1", "policy2");
@@ -46,7 +47,7 @@ void testPolicyInfoCreation() {
4647
@Test
4748
@DisplayName("Should handle null lists")
4849
void testPolicyInfoNullLists() {
49-
PolicyInfo info = new PolicyInfo(null, null, "10ms", "tenant", null);
50+
PolicyInfo info = new PolicyInfo(null, null, "10ms", "tenant", null, null);
5051

5152
assertThat(info.getPoliciesEvaluated()).isEmpty();
5253
assertThat(info.getStaticChecks()).isEmpty();
@@ -55,7 +56,7 @@ void testPolicyInfoNullLists() {
5556
@Test
5657
@DisplayName("getProcessingDuration should parse milliseconds")
5758
void testProcessingDurationMilliseconds() {
58-
PolicyInfo info = new PolicyInfo(null, null, "17.48ms", null, null);
59+
PolicyInfo info = new PolicyInfo(null, null, "17.48ms", null, null, null);
5960

6061
Duration duration = info.getProcessingDuration();
6162
assertThat(duration.toNanos()).isGreaterThan(17_000_000L);
@@ -65,7 +66,7 @@ void testProcessingDurationMilliseconds() {
6566
@Test
6667
@DisplayName("getProcessingDuration should parse seconds")
6768
void testProcessingDurationSeconds() {
68-
PolicyInfo info = new PolicyInfo(null, null, "1.5s", null, null);
69+
PolicyInfo info = new PolicyInfo(null, null, "1.5s", null, null, null);
6970

7071
Duration duration = info.getProcessingDuration();
7172
assertThat(duration.toMillis()).isGreaterThanOrEqualTo(1500L);
@@ -75,7 +76,7 @@ void testProcessingDurationSeconds() {
7576
@DisplayName("getProcessingDuration should parse microseconds (us)")
7677
void testProcessingDurationMicroseconds() {
7778
// Note: the implementation may not handle 'us' suffix perfectly
78-
PolicyInfo info = new PolicyInfo(null, null, "500µs", null, null);
79+
PolicyInfo info = new PolicyInfo(null, null, "500µs", null, null, null);
7980

8081
Duration duration = info.getProcessingDuration();
8182
// If µs parsing works, we get microseconds; otherwise it falls through
@@ -85,7 +86,7 @@ void testProcessingDurationMicroseconds() {
8586
@Test
8687
@DisplayName("getProcessingDuration should handle ns suffix")
8788
void testProcessingDurationNanoseconds() {
88-
PolicyInfo info = new PolicyInfo(null, null, "1000ns", null, null);
89+
PolicyInfo info = new PolicyInfo(null, null, "1000ns", null, null, null);
8990

9091
Duration duration = info.getProcessingDuration();
9192
// Implementation may return ZERO if parsing fails
@@ -95,24 +96,24 @@ void testProcessingDurationNanoseconds() {
9596
@Test
9697
@DisplayName("getProcessingDuration should handle empty/null")
9798
void testProcessingDurationEmpty() {
98-
PolicyInfo info1 = new PolicyInfo(null, null, null, null, null);
99+
PolicyInfo info1 = new PolicyInfo(null, null, null, null, null, null);
99100
assertThat(info1.getProcessingDuration()).isEqualTo(Duration.ZERO);
100101

101-
PolicyInfo info2 = new PolicyInfo(null, null, "", null, null);
102+
PolicyInfo info2 = new PolicyInfo(null, null, "", null, null, null);
102103
assertThat(info2.getProcessingDuration()).isEqualTo(Duration.ZERO);
103104
}
104105

105106
@Test
106107
@DisplayName("getProcessingDuration should handle invalid format")
107108
void testProcessingDurationInvalid() {
108-
PolicyInfo info = new PolicyInfo(null, null, "invalid", null, null);
109+
PolicyInfo info = new PolicyInfo(null, null, "invalid", null, null, null);
109110
assertThat(info.getProcessingDuration()).isEqualTo(Duration.ZERO);
110111
}
111112

112113
@Test
113114
@DisplayName("getProcessingDuration should handle raw number as milliseconds")
114115
void testProcessingDurationRawNumber() {
115-
PolicyInfo info = new PolicyInfo(null, null, "100", null, null);
116+
PolicyInfo info = new PolicyInfo(null, null, "100", null, null, null);
116117

117118
Duration duration = info.getProcessingDuration();
118119
assertThat(duration.toMillis()).isEqualTo(100L);
@@ -126,23 +127,26 @@ void testPolicyInfoEqualsHashCode() {
126127
List.of("check1"),
127128
"10ms",
128129
"tenant1",
129-
0.5
130+
0.5,
131+
null
130132
);
131133

132134
PolicyInfo info2 = new PolicyInfo(
133135
List.of("policy1"),
134136
List.of("check1"),
135137
"10ms",
136138
"tenant1",
137-
0.5
139+
0.5,
140+
null
138141
);
139142

140143
PolicyInfo info3 = new PolicyInfo(
141144
List.of("policy2"),
142145
List.of("check1"),
143146
"10ms",
144147
"tenant1",
145-
0.5
148+
0.5,
149+
null
146150
);
147151

148152
assertThat(info1).isEqualTo(info2);
@@ -161,7 +165,8 @@ void testPolicyInfoToString() {
161165
List.of("check1"),
162166
"10ms",
163167
"tenant1",
164-
0.5
168+
0.5,
169+
null
165170
);
166171

167172
String str = info.toString();

0 commit comments

Comments
 (0)