Java SDK
Official Java SDK for Kai the AI
Overview
The official Java SDK for Kai the AI provides a robust, enterprise-ready interface for integrating intelligent teaching assistance into your Java applications. Built with Java 11+ features, the SDK supports both synchronous and asynchronous operations, comprehensive error handling, thread safety, and follows modern Java best practices.
Installation
Add the SDK to your project using Maven or Gradle:
<dependency>
<groupId>com.chi2labs</groupId>
<artifactId>kai-sdk</artifactId>
<version>1.0.0</version>
</dependency>dependencies {
implementation 'com.chi2labs:kai-sdk:1.0.0'
}dependencies {
implementation("com.chi2labs:kai-sdk:1.0.0")
}Requirements
- Java 11 or higher
- Active Kai the AI API key
- Internet connection for API requests
Quick Start
Here’s a minimal example to get you started:
import com.chi2labs.kai.KaiClient;
import com.chi2labs.kai.models.GradeResult;
public class QuickStart {
public static void main(String[] args) {
// Initialize the client
KaiClient client = new KaiClient("your_api_key_here");
// Grade a student submission
GradeResult result = client.assignments().grade()
.assignmentId("CS101-HW1")
.studentSubmission("The sorting algorithm works by...")
.gradingStyle("detailed")
.execute();
System.out.printf("Score: %d/100%n", result.getScore());
System.out.printf("Feedback: %s%n", result.getFeedback());
}
}Authentication
API Key Setup
The SDK requires an API key for authentication. You can provide the key in several ways:
export KAI_API_KEY="your_api_key_here"import com.chi2labs.kai.KaiClient;
// Automatically reads from KAI_API_KEY environment variable
KaiClient client = new KaiClient();import com.chi2labs.kai.KaiClient;
KaiClient client = new KaiClient("your_api_key_here");import com.chi2labs.kai.KaiClient;
import com.chi2labs.kai.config.KaiConfig;
KaiConfig config = KaiConfig.builder()
.apiKey("your_api_key_here")
.baseUrl("https://chi2api.com/v1")
.timeout(30000)
.build();
KaiClient client = new KaiClient(config);# kai.properties
kai.api.key=your_api_key_here
kai.api.baseUrl=https://chi2api.com/v1
kai.api.timeout=30000
import com.chi2labs.kai.KaiClient;
import java.util.Properties;
import java.io.FileInputStream;
Properties props = new Properties();
props.load(new FileInputStream("kai.properties"));
KaiClient client = new KaiClient(props.getProperty("kai.api.key"));Never hardcode API keys in your source code or commit them to version control. Use environment variables or secure configuration management systems like AWS Secrets Manager or HashiCorp Vault.
Core Classes
KaiClient
The main client class for interacting with Kai’s API.
public class KaiClient implements AutoCloseable {
/**
* Create a new KaiClient with the specified API key.
*
* @param apiKey Your API key (or null to read from KAI_API_KEY env variable)
*/
public KaiClient(String apiKey) { }
/**
* Create a new KaiClient with custom configuration.
*
* @param config Configuration object
*/
public KaiClient(KaiConfig config) { }
/**
* Access assignment grading operations.
*
* @return AssignmentOperations instance
*/
public AssignmentOperations assignments() { }
/**
* Access content generation operations.
*
* @return ContentOperations instance
*/
public ContentOperations content() { }
/**
* Access student analytics operations.
*
* @return AnalyticsOperations instance
*/
public AnalyticsOperations analytics() { }
/**
* Access quiz generation operations.
*
* @return QuizOperations instance
*/
public QuizOperations quizzes() { }
@Override
public void close() { }
}Usage with try-with-resources:
try (KaiClient client = new KaiClient()) {
GradeResult result = client.assignments().grade()
.assignmentId("CS101-HW1")
.studentSubmission("Student work...")
.execute();
System.out.println("Score: " + result.getScore());
}AssignmentOperations
Handle assignment grading and feedback operations.
public interface AssignmentOperations {
/**
* Create a new grade request builder.
*
* @return GradeRequestBuilder for fluent API
*/
GradeRequestBuilder grade();
/**
* Retrieve a previously generated grade.
*
* @param gradeId The grade identifier
* @return GradeResult object
* @throws KaiException if grade not found or API error
*/
GradeResult getGrade(String gradeId) throws KaiException;
/**
* Grade multiple submissions in a single request.
*
* @param submissions List of submission data
* @return List of GradeResult objects
* @throws KaiException if batch grading fails
*/
List<GradeResult> batchGrade(List<SubmissionData> submissions)
throws KaiException;
}GradeRequestBuilder
Fluent API for building grade requests:
public interface GradeRequestBuilder {
GradeRequestBuilder assignmentId(String assignmentId);
GradeRequestBuilder studentSubmission(String submission);
GradeRequestBuilder rubricId(String rubricId);
GradeRequestBuilder gradingStyle(String style);
GradeResult execute() throws KaiException;
CompletableFuture<GradeResult> executeAsync();
}QuizOperations
Generate and manage quiz content.
public interface QuizOperations {
/**
* Create a new quiz generation request builder.
*
* @return QuizRequestBuilder for fluent API
*/
QuizRequestBuilder generate();
/**
* Retrieve a previously generated quiz.
*
* @param quizId The quiz identifier
* @return QuizResult object
* @throws KaiException if quiz not found or API error
*/
QuizResult getQuiz(String quizId) throws KaiException;
}
public interface QuizRequestBuilder {
QuizRequestBuilder topic(String topic);
QuizRequestBuilder difficulty(String difficulty);
QuizRequestBuilder questionCount(int count);
QuizRequestBuilder questionTypes(String... types);
QuizResult execute() throws KaiException;
CompletableFuture<QuizResult> executeAsync();
}AnalyticsOperations
Access student performance data and analytics.
public interface AnalyticsOperations {
/**
* Retrieve student performance metrics.
*
* @param studentId Student identifier
* @return PerformanceData with metrics and trends
* @throws KaiException if analytics retrieval fails
*/
PerformanceData getStudentPerformance(String studentId)
throws KaiException;
/**
* Retrieve student performance metrics with filters.
*
* @param request Performance request with filters
* @return PerformanceData with metrics and trends
* @throws KaiException if analytics retrieval fails
*/
PerformanceData getStudentPerformance(PerformanceRequest request)
throws KaiException;
/**
* Retrieve aggregate class-level analytics.
*
* @param courseId Course identifier
* @return ClassAnalytics object
* @throws KaiException if analytics retrieval fails
*/
ClassAnalytics getClassAnalytics(String courseId)
throws KaiException;
}Response Models
GradeResult
public class GradeResult {
private final String gradeId;
private final int score;
private final String feedback;
private final List<String> suggestions;
private final Instant timestamp;
private final Map<String, Integer> rubricBreakdown;
// Getters
public String getGradeId() { return gradeId; }
public int getScore() { return score; }
public String getFeedback() { return feedback; }
public List<String> getSuggestions() { return suggestions; }
public Instant getTimestamp() { return timestamp; }
public Map<String, Integer> getRubricBreakdown() { return rubricBreakdown; }
/**
* Check if grade meets passing threshold (>= 70).
*
* @return true if passing, false otherwise
*/
public boolean isPassed() {
return score >= 70;
}
@Override
public String toString() {
return String.format("GradeResult{gradeId='%s', score=%d, passed=%s}",
gradeId, score, isPassed());
}
}QuizResult
public class QuizQuestion {
private final String questionId;
private final String questionText;
private final String questionType;
private final List<String> options;
private final String correctAnswer;
private final String explanation;
// Getters omitted for brevity
}
public class QuizResult {
private final String quizId;
private final String topic;
private final String difficulty;
private final List<QuizQuestion> questions;
private final int estimatedTimeMinutes;
// Getters
public String getQuizId() { return quizId; }
public String getTopic() { return topic; }
public String getDifficulty() { return difficulty; }
public List<QuizQuestion> getQuestions() { return questions; }
public int getEstimatedTimeMinutes() { return estimatedTimeMinutes; }
/**
* Get the total number of questions in the quiz.
*
* @return number of questions
*/
public int getQuestionCount() {
return questions.size();
}
}PerformanceData
public class PerformanceData {
private final String studentId;
private final double averageScore;
private final int assignmentsCompleted;
private final int assignmentsTotal;
private final List<String> strengthAreas;
private final List<String> improvementAreas;
private final String trend;
private final Instant lastUpdated;
// Getters
public String getStudentId() { return studentId; }
public double getAverageScore() { return averageScore; }
public int getAssignmentsCompleted() { return assignmentsCompleted; }
public int getAssignmentsTotal() { return assignmentsTotal; }
public List<String> getStrengthAreas() { return strengthAreas; }
public List<String> getImprovementAreas() { return improvementAreas; }
public String getTrend() { return trend; }
public Instant getLastUpdated() { return lastUpdated; }
/**
* Calculate completion rate as a percentage.
*
* @return completion rate (0-100)
*/
public double getCompletionRate() {
return (double) assignmentsCompleted / assignmentsTotal * 100;
}
}Complete API Methods
Assignment Grading
import com.chi2labs.kai.KaiClient;
import com.chi2labs.kai.models.GradeResult;
import com.chi2labs.kai.exceptions.*;
public class GradingExample {
public static void main(String[] args) {
try (KaiClient client = new KaiClient()) {
// Grade a single submission
GradeResult result = client.assignments().grade()
.assignmentId("CS101-HW1")
.studentSubmission("My algorithm implementation...")
.rubricId("standard_coding_rubric")
.gradingStyle("detailed")
.execute();
System.out.printf("Grade ID: %s%n", result.getGradeId());
System.out.printf("Score: %d/100%n", result.getScore());
System.out.printf("Feedback: %s%n", result.getFeedback());
if (result.getRubricBreakdown() != null) {
System.out.println("\nRubric Breakdown:");
result.getRubricBreakdown().forEach((criterion, points) ->
System.out.printf(" %s: %d%n", criterion, points)
);
}
System.out.println("\nSuggestions:");
result.getSuggestions().forEach(suggestion ->
System.out.println(" - " + suggestion)
);
} catch (ValidationException e) {
System.err.println("Invalid parameters: " + e.getMessage());
} catch (RateLimitException e) {
System.err.println("Rate limit exceeded: " + e.getMessage());
} catch (KaiException e) {
System.err.println("API error: " + e.getMessage());
}
}
}import com.chi2labs.kai.KaiClient;
import com.chi2labs.kai.models.GradeResult;
import java.util.concurrent.CompletableFuture;
public class AsyncGradingExample {
public static void main(String[] args) {
try (KaiClient client = new KaiClient()) {
CompletableFuture<GradeResult> future = client.assignments().grade()
.assignmentId("CS101-HW1")
.studentSubmission("My algorithm implementation...")
.gradingStyle("detailed")
.executeAsync();
// Handle result asynchronously
future.thenAccept(result -> {
System.out.printf("Score: %d/100%n", result.getScore());
System.out.println("Feedback: " + result.getFeedback());
}).exceptionally(ex -> {
System.err.println("Grading failed: " + ex.getMessage());
return null;
});
// Wait for completion (in real app, do other work)
future.join();
}
}
}import com.chi2labs.kai.KaiClient;
import com.chi2labs.kai.models.GradeResult;
import java.util.concurrent.CompletableFuture;
import java.util.List;
public class ComposedAsyncExample {
public static void main(String[] args) {
try (KaiClient client = new KaiClient()) {
List<String> submissions = List.of(
"First submission...",
"Second submission...",
"Third submission..."
);
// Grade all submissions concurrently
CompletableFuture<?>[] futures = submissions.stream()
.map(submission -> client.assignments().grade()
.assignmentId("CS101-HW1")
.studentSubmission(submission)
.executeAsync()
.thenAccept(result ->
System.out.printf("Score: %d/100%n", result.getScore())
))
.toArray(CompletableFuture[]::new);
// Wait for all to complete
CompletableFuture.allOf(futures).join();
System.out.println("All grading completed!");
}
}
}Batch Grading
Grade multiple submissions efficiently:
import com.chi2labs.kai.KaiClient;
import com.chi2labs.kai.models.GradeResult;
import com.chi2labs.kai.models.SubmissionData;
import java.util.List;
public class BatchGradingExample {
public static void main(String[] args) {
try (KaiClient client = new KaiClient()) {
List<SubmissionData> submissions = List.of(
new SubmissionData("CS101-HW1", "student_001", "First student's work..."),
new SubmissionData("CS101-HW1", "student_002", "Second student's work..."),
new SubmissionData("CS101-HW1", "student_003", "Third student's work...")
);
List<GradeResult> results = client.assignments().batchGrade(submissions);
results.forEach(result ->
System.out.printf("Student: Score %d/100%n", result.getScore())
);
// Calculate class statistics
double averageScore = results.stream()
.mapToInt(GradeResult::getScore)
.average()
.orElse(0.0);
System.out.printf("Class average: %.1f/100%n", averageScore);
} catch (Exception e) {
System.err.println("Batch grading failed: " + e.getMessage());
}
}
}Quiz Generation
import com.chi2labs.kai.KaiClient;
import com.chi2labs.kai.models.QuizResult;
import com.chi2labs.kai.models.QuizQuestion;
public class QuizExample {
public static void main(String[] args) {
try (KaiClient client = new KaiClient()) {
QuizResult quiz = client.quizzes().generate()
.topic("Java Data Structures")
.difficulty("medium")
.questionCount(10)
.questionTypes("multiple_choice", "true_false")
.execute();
System.out.printf("Quiz ID: %s%n", quiz.getQuizId());
System.out.printf("Estimated time: %d minutes%n",
quiz.getEstimatedTimeMinutes());
System.out.printf("Questions: %d%n", quiz.getQuestionCount());
int questionNum = 1;
for (QuizQuestion question : quiz.getQuestions()) {
System.out.printf("%nQuestion %d: %s%n",
questionNum++, question.getQuestionText());
if (question.getOptions() != null) {
question.getOptions().forEach(opt ->
System.out.println(" " + opt)
);
}
}
} catch (Exception e) {
System.err.println("Quiz generation failed: " + e.getMessage());
}
}
}import com.chi2labs.kai.KaiClient;
import com.chi2labs.kai.models.QuizResult;
import java.util.List;
import java.util.concurrent.CompletableFuture;
public class MultipleQuizzesExample {
public static void main(String[] args) {
try (KaiClient client = new KaiClient()) {
List<String> topics = List.of(
"Java Basics",
"Object-Oriented Programming",
"Data Structures"
);
// Generate multiple quizzes concurrently
CompletableFuture<?>[] futures = topics.stream()
.map(topic -> client.quizzes().generate()
.topic(topic)
.difficulty("medium")
.questionCount(5)
.executeAsync()
.thenAccept(quiz ->
System.out.printf("Generated quiz: %s (%d questions)%n",
quiz.getTopic(), quiz.getQuestionCount())
))
.toArray(CompletableFuture[]::new);
CompletableFuture.allOf(futures).join();
System.out.printf("Generated %d quizzes%n", topics.size());
} catch (Exception e) {
System.err.println("Quiz generation failed: " + e.getMessage());
}
}
}Student Analytics
import com.chi2labs.kai.KaiClient;
import com.chi2labs.kai.models.PerformanceData;
import com.chi2labs.kai.models.PerformanceRequest;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
public class AnalyticsExample {
public static void main(String[] args) {
try (KaiClient client = new KaiClient()) {
// Get performance for the last 30 days
Instant endDate = Instant.now();
Instant startDate = endDate.minus(30, ChronoUnit.DAYS);
PerformanceRequest request = new PerformanceRequest.Builder()
.studentId("student_12345")
.startDate(startDate)
.endDate(endDate)
.courseId("CS101")
.build();
PerformanceData performance = client.analytics()
.getStudentPerformance(request);
System.out.printf("Average Score: %.1f%n",
performance.getAverageScore());
System.out.printf("Completion Rate: %d/%d (%.1f%%)%n",
performance.getAssignmentsCompleted(),
performance.getAssignmentsTotal(),
performance.getCompletionRate());
System.out.printf("Trend: %s%n", performance.getTrend());
System.out.println("\nStrengths:");
performance.getStrengthAreas().forEach(area ->
System.out.println(" ✓ " + area)
);
System.out.println("\nAreas for Improvement:");
performance.getImprovementAreas().forEach(area ->
System.out.println(" ! " + area)
);
} catch (Exception e) {
System.err.println("Analytics retrieval failed: " + e.getMessage());
}
}
}Error Handling
The SDK provides specific exception types for different error scenarios:
import com.chi2labs.kai.KaiClient;
import com.chi2labs.kai.models.GradeResult;
import com.chi2labs.kai.exceptions.*;
public class ErrorHandlingExample {
public static void main(String[] args) {
try (KaiClient client = new KaiClient()) {
GradeResult result = client.assignments().grade()
.assignmentId("CS101-HW1")
.studentSubmission("Student work...")
.gradingStyle("detailed")
.execute();
System.out.println("Score: " + result.getScore());
} catch (ValidationException e) {
// Handle invalid parameters
System.err.println("Invalid input: " + e.getMessage());
System.err.println("Details: " + e.getDetails());
} catch (AuthenticationException e) {
// Handle authentication failures
System.err.println("Authentication failed: " + e.getMessage());
System.err.println("Please check your API key");
} catch (RateLimitException e) {
// Handle rate limiting
System.err.println("Rate limit exceeded: " + e.getMessage());
System.err.printf("Retry after: %d seconds%n",
e.getRetryAfterSeconds());
} catch (TimeoutException e) {
// Handle timeouts
System.err.println("Request timed out: " + e.getMessage());
System.err.println("Try increasing the timeout parameter");
} catch (ApiException e) {
// Handle general API errors
System.err.println("API error: " + e.getMessage());
System.err.println("Status code: " + e.getStatusCode());
System.err.println("Error code: " + e.getErrorCode());
} catch (KaiException e) {
// Catch-all for SDK errors
System.err.println("SDK error: " + e.getMessage());
}
}
}Exception Hierarchy
KaiException (base)
├── ValidationException // Invalid parameters
├── AuthenticationException // Invalid API key
├── RateLimitException // Rate limit exceeded
├── TimeoutException // Request timeout
└── ApiException // General API errorImplement exponential backoff for rate limit errors:
import com.chi2labs.kai.exceptions.RateLimitException;
public GradeResult gradeWithRetry(KaiClient client, String assignmentId,
String submission) throws KaiException {
int maxRetries = 3;
int retryDelay = 1000; // milliseconds
for (int attempt = 0; attempt < maxRetries; attempt++) {
try {
return client.assignments().grade()
.assignmentId(assignmentId)
.studentSubmission(submission)
.execute();
} catch (RateLimitException e) {
if (attempt < maxRetries - 1) {
int waitTime = retryDelay * (int) Math.pow(2, attempt);
System.out.printf("Rate limited, waiting %dms...%n", waitTime);
Thread.sleep(waitTime);
} else {
throw e;
}
}
}
throw new KaiException("Max retries exceeded");
}Thread Safety
The KaiClient is thread-safe and can be shared across multiple threads:
import com.chi2labs.kai.KaiClient;
import java.util.concurrent.*;
public class ThreadSafetyExample {
private static final KaiClient client = new KaiClient();
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(10);
// Submit multiple grading tasks concurrently
for (int i = 0; i < 100; i++) {
final int index = i;
executor.submit(() -> {
try {
var result = client.assignments().grade()
.assignmentId("CS101-HW" + index)
.studentSubmission("Submission " + index)
.execute();
System.out.printf("Thread %s: Score %d%n",
Thread.currentThread().getName(),
result.getScore());
} catch (Exception e) {
System.err.println("Error in thread: " + e.getMessage());
}
});
}
executor.shutdown();
executor.awaitTermination(5, TimeUnit.MINUTES);
client.close();
}
}The SDK uses connection pooling by default for efficient resource usage. You can configure pool settings:
KaiConfig config = KaiConfig.builder()
.apiKey("your_api_key")
.maxConnections(50)
.connectionTimeout(30000)
.build();
KaiClient client = new KaiClient(config);Configuration Options
Client Configuration
import com.chi2labs.kai.KaiClient;
import com.chi2labs.kai.config.KaiConfig;
import java.time.Duration;
// Method 1: Builder pattern
KaiConfig config = KaiConfig.builder()
.apiKey("your_api_key")
.baseUrl("https://chi2api.com/v1")
.timeout(Duration.ofSeconds(30))
.maxRetries(3)
.maxConnections(20)
.verifySSL(true)
.userAgent("MyApp/1.0")
.build();
KaiClient client = new KaiClient(config);
// Method 2: From properties file
KaiClient client = KaiClient.fromProperties("kai.properties");
// Method 3: From environment variables
KaiClient client = KaiClient.fromEnvironment();Logging Configuration
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.chi2labs.kai.KaiClient;
public class LoggingExample {
private static final Logger logger = LoggerFactory.getLogger(LoggingExample.class);
public static void main(String[] args) {
// The SDK uses SLF4J for logging
// Configure your logging framework (Logback, Log4j2, etc.)
try (KaiClient client = new KaiClient()) {
logger.info("Initializing Kai client");
var result = client.assignments().grade()
.assignmentId("CS101-HW1")
.studentSubmission("Student work...")
.execute();
logger.info("Grading completed: score={}", result.getScore());
}
}
}Logback configuration (logback.xml):
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- Set SDK logging level -->
<logger name="com.chi2labs.kai" level="DEBUG"/>
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
</configuration>Spring Framework Integration
Spring Boot Configuration
import com.chi2labs.kai.KaiClient;
import com.chi2labs.kai.config.KaiConfig;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class KaiConfiguration {
@Bean
@ConfigurationProperties(prefix = "kai")
public KaiConfig kaiConfig() {
return KaiConfig.builder().build();
}
@Bean
public KaiClient kaiClient(KaiConfig config) {
return new KaiClient(config);
}
}application.yml:
kai:
api-key: ${KAI_API_KEY}
base-url: https://chi2api.com/v1
timeout: 30000
max-retries: 3Spring Service Example
import com.chi2labs.kai.KaiClient;
import com.chi2labs.kai.models.GradeResult;
import com.chi2labs.kai.exceptions.KaiException;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
@Service
public class GradingService {
private final KaiClient kaiClient;
@Autowired
public GradingService(KaiClient kaiClient) {
this.kaiClient = kaiClient;
}
public GradeResult gradeAssignment(String assignmentId, String submission) {
try {
return kaiClient.assignments().grade()
.assignmentId(assignmentId)
.studentSubmission(submission)
.gradingStyle("detailed")
.execute();
} catch (KaiException e) {
throw new RuntimeException("Grading failed", e);
}
}
public CompletableFuture<GradeResult> gradeAssignmentAsync(
String assignmentId, String submission) {
return kaiClient.assignments().grade()
.assignmentId(assignmentId)
.studentSubmission(submission)
.executeAsync();
}
}Spring REST Controller
import com.chi2labs.kai.models.GradeResult;
import org.springframework.web.bind.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
@RestController
@RequestMapping("/api/grading")
public class GradingController {
private final GradingService gradingService;
@Autowired
public GradingController(GradingService gradingService) {
this.gradingService = gradingService;
}
@PostMapping("/grade")
public ResponseEntity<GradeResult> gradeSubmission(
@RequestBody SubmissionRequest request) {
GradeResult result = gradingService.gradeAssignment(
request.getAssignmentId(),
request.getSubmission()
);
return ResponseEntity.ok(result);
}
@PostMapping("/grade/async")
public CompletableFuture<ResponseEntity<GradeResult>> gradeSubmissionAsync(
@RequestBody SubmissionRequest request) {
return gradingService.gradeAssignmentAsync(
request.getAssignmentId(),
request.getSubmission()
).thenApply(ResponseEntity::ok);
}
}Complete Working Examples
Example 1: Automated Grading Pipeline
import com.chi2labs.kai.KaiClient;
import com.chi2labs.kai.models.GradeResult;
import com.chi2labs.kai.models.SubmissionData;
import com.chi2labs.kai.exceptions.KaiException;
import java.io.*;
import java.nio.file.*;
import java.util.*;
import java.util.stream.Collectors;
/**
* Automated grading pipeline for processing student submissions from CSV.
*/
public class AutomatedGradingPipeline {
private final KaiClient client;
public AutomatedGradingPipeline(KaiClient client) {
this.client = client;
}
public static class Submission {
private final String studentId;
private final String assignmentId;
private final String submissionText;
public Submission(String studentId, String assignmentId, String submissionText) {
this.studentId = studentId;
this.assignmentId = assignmentId;
this.submissionText = submissionText;
}
// Getters
public String getStudentId() { return studentId; }
public String getAssignmentId() { return assignmentId; }
public String getSubmissionText() { return submissionText; }
}
public static class GradingRecord {
private final String studentId;
private final Integer score;
private final String feedback;
private final String suggestions;
public GradingRecord(String studentId, Integer score,
String feedback, String suggestions) {
this.studentId = studentId;
this.score = score;
this.feedback = feedback;
this.suggestions = suggestions;
}
// Getters omitted for brevity
}
public List<Submission> loadSubmissions(Path csvPath) throws IOException {
List<Submission> submissions = new ArrayList<>();
try (BufferedReader reader = Files.newBufferedReader(csvPath)) {
String line = reader.readLine(); // Skip header
while ((line = reader.readLine()) != null) {
String[] parts = line.split(",", 3);
if (parts.length == 3) {
submissions.add(new Submission(
parts[0].trim(),
parts[1].trim(),
parts[2].trim()
));
}
}
}
return submissions;
}
public List<GradingRecord> gradeSubmissions(List<Submission> submissions) {
List<GradingRecord> records = new ArrayList<>();
for (Submission submission : submissions) {
try {
GradeResult result = client.assignments().grade()
.assignmentId(submission.getAssignmentId())
.studentSubmission(submission.getSubmissionText())
.gradingStyle("detailed")
.execute();
records.add(new GradingRecord(
submission.getStudentId(),
result.getScore(),
result.getFeedback(),
String.join(", ", result.getSuggestions())
));
System.out.printf("✓ Graded %s: %d/100%n",
submission.getStudentId(), result.getScore());
} catch (KaiException e) {
System.err.printf("✗ Failed to grade %s: %s%n",
submission.getStudentId(), e.getMessage());
records.add(new GradingRecord(
submission.getStudentId(),
null,
"Error: " + e.getMessage(),
""
));
}
}
return records;
}
public void saveResults(List<GradingRecord> records, Path outputPath)
throws IOException {
try (PrintWriter writer = new PrintWriter(Files.newBufferedWriter(outputPath))) {
writer.println("student_id,score,feedback,suggestions");
for (GradingRecord record : records) {
writer.printf("%s,%s,\"%s\",\"%s\"%n",
record.studentId,
record.score != null ? record.score : "",
record.feedback.replace("\"", "\"\""),
record.suggestions.replace("\"", "\"\"")
);
}
}
System.out.println("\n✓ Results saved to " + outputPath);
}
public static void main(String[] args) {
try (KaiClient client = new KaiClient()) {
AutomatedGradingPipeline pipeline = new AutomatedGradingPipeline(client);
// Load submissions
Path inputPath = Paths.get("submissions.csv");
List<Submission> submissions = pipeline.loadSubmissions(inputPath);
System.out.printf("Loaded %d submissions%n", submissions.size());
// Grade all submissions
List<GradingRecord> results = pipeline.gradeSubmissions(submissions);
// Save results
Path outputPath = Paths.get("grading_results.csv");
pipeline.saveResults(results, outputPath);
// Print summary
long successful = results.stream()
.filter(r -> r.score != null)
.count();
double avgScore = results.stream()
.filter(r -> r.score != null)
.mapToInt(r -> r.score)
.average()
.orElse(0.0);
System.out.println("\n=== Summary ===");
System.out.printf("Total submissions: %d%n", submissions.size());
System.out.printf("Successfully graded: %d%n", successful);
System.out.printf("Average score: %.1f/100%n", avgScore);
} catch (Exception e) {
System.err.println("Pipeline failed: " + e.getMessage());
e.printStackTrace();
}
}
}Example 2: Quiz Generator with Export
import com.chi2labs.kai.KaiClient;
import com.chi2labs.kai.models.QuizResult;
import com.chi2labs.kai.models.QuizQuestion;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.*;
import java.nio.file.*;
import java.util.*;
/**
* Generate quizzes for multiple topics and export to various formats.
*/
public class QuizGenerator {
private final KaiClient client;
private final Gson gson;
public QuizGenerator(KaiClient client) {
this.client = client;
this.gson = new GsonBuilder().setPrettyPrinting().create();
}
public QuizResult generateQuiz(String topic, String difficulty, int count)
throws Exception {
QuizResult quiz = client.quizzes().generate()
.topic(topic)
.difficulty(difficulty)
.questionCount(count)
.questionTypes("multiple_choice", "true_false", "short_answer")
.execute();
System.out.printf("%n✓ Generated quiz: %s%n", topic);
System.out.printf(" ID: %s%n", quiz.getQuizId());
System.out.printf(" Questions: %d%n", quiz.getQuestionCount());
System.out.printf(" Time: ~%d minutes%n", quiz.getEstimatedTimeMinutes());
return quiz;
}
public void exportToJson(QuizResult quiz, String filename) throws IOException {
Map<String, Object> quizData = new HashMap<>();
quizData.put("quiz_id", quiz.getQuizId());
quizData.put("topic", quiz.getTopic());
quizData.put("difficulty", quiz.getDifficulty());
quizData.put("estimated_time_minutes", quiz.getEstimatedTimeMinutes());
List<Map<String, Object>> questions = new ArrayList<>();
for (QuizQuestion q : quiz.getQuestions()) {
Map<String, Object> questionData = new HashMap<>();
questionData.put("id", q.getQuestionId());
questionData.put("text", q.getQuestionText());
questionData.put("type", q.getQuestionType());
questionData.put("options", q.getOptions());
questionData.put("correct_answer", q.getCorrectAnswer());
questionData.put("explanation", q.getExplanation());
questions.add(questionData);
}
quizData.put("questions", questions);
String json = gson.toJson(quizData);
Files.writeString(Paths.get(filename), json);
System.out.println("✓ Exported to " + filename);
}
public void exportToMarkdown(QuizResult quiz, String filename) throws IOException {
StringBuilder md = new StringBuilder();
md.append("# ").append(quiz.getTopic()).append(" Quiz\n\n");
md.append("**Difficulty:** ").append(quiz.getDifficulty()).append("\n");
md.append("**Estimated Time:** ").append(quiz.getEstimatedTimeMinutes())
.append(" minutes\n\n");
md.append("---\n\n");
int i = 1;
for (QuizQuestion q : quiz.getQuestions()) {
md.append("## Question ").append(i++).append("\n\n");
md.append(q.getQuestionText()).append("\n\n");
if (q.getOptions() != null) {
for (String opt : q.getOptions()) {
md.append("- ").append(opt).append("\n");
}
md.append("\n");
}
if (q.getExplanation() != null) {
md.append("**Explanation:** ").append(q.getExplanation()).append("\n\n");
}
md.append("---\n\n");
}
Files.writeString(Paths.get(filename), md.toString());
System.out.println("✓ Exported to " + filename);
}
public static void main(String[] args) {
try (KaiClient client = new KaiClient()) {
QuizGenerator generator = new QuizGenerator(client);
// Topics to generate quizzes for
List<TopicConfig> topics = List.of(
new TopicConfig("Java Fundamentals", "easy", 15),
new TopicConfig("Data Structures", "medium", 20),
new TopicConfig("Algorithm Design", "hard", 10)
);
for (TopicConfig config : topics) {
// Generate quiz
QuizResult quiz = generator.generateQuiz(
config.topic,
config.difficulty,
config.count
);
// Export to different formats
String baseName = config.topic.toLowerCase().replace(" ", "_");
generator.exportToJson(quiz, baseName + "_quiz.json");
generator.exportToMarkdown(quiz, baseName + "_quiz.md");
}
System.out.println("\n✓ All quizzes generated and exported successfully!");
} catch (Exception e) {
System.err.println("Quiz generation failed: " + e.getMessage());
e.printStackTrace();
}
}
private static class TopicConfig {
final String topic;
final String difficulty;
final int count;
TopicConfig(String topic, String difficulty, int count) {
this.topic = topic;
this.difficulty = difficulty;
this.count = count;
}
}
}Best Practices
Reuse KaiClient Instance: Create once and share across your application
// Good: Single instance private static final KaiClient client = new KaiClient(); // Bad: Creating new instances new KaiClient().assignments().grade()...Use Batch Operations: Grade multiple submissions in one request
List<GradeResult> results = client.assignments().batchGrade(submissions);Enable Async for Concurrent Operations: Use
executeAsync()for parallel requestsCompletableFuture<?>[] futures = submissions.stream() .map(s -> client.assignments().grade() .assignmentId(s.getId()) .studentSubmission(s.getText()) .executeAsync()) .toArray(CompletableFuture[]::new); CompletableFuture.allOf(futures).join();Implement Caching: Cache frequently accessed data
private final LoadingCache<String, RubricData> rubricCache = CacheBuilder.newBuilder() .maximumSize(100) .expireAfterWrite(1, TimeUnit.HOURS) .build(new CacheLoader<String, RubricData>() { public RubricData load(String rubricId) { return client.rubrics().get(rubricId); } });Monitor Rate Limits: Check response headers
GradeResult result = client.assignments().grade()...execute(); int remaining = client.getLastResponse() .header("X-RateLimit-Remaining") .map(Integer::parseInt) .orElse(0);Use Try-With-Resources: Ensure proper cleanup
try (KaiClient client = new KaiClient()) { // Use client } // Automatically closed
Links and Resources
SDK Resources
- GitHub Repository - Source code and issues
- Maven Central - Package distribution
- Javadoc - API documentation
- Changelog - Version history
- Examples - More code examples
Support
- API Status: status.kaitheai.com
- Developer Forum: forum.kaitheai.com
- Email: developers@chi2labs.com
Star the GitHub repository to receive notifications about new releases and features.