v0.4.md
1 # Oxyjen v0.4 documentation 2 ## Typed, Compile-Time-Safe AI Pipelines 3 4 # What's New in v0.4 5 6 ## 1. Typed LLM Outputs 7 8 You can now define: 9 10 ``` java 11 SchemaNode<User> 12 ``` 13 14 ...and receive a `User` object directly, not a string. 15 16 - No manual parsing. 17 - No fragile regex. 18 - No unsafe casting. 19 20 ------------------------------------------------------------------------ 21 22 ## 2. JSON Schema Generation from Java Classes 23 24 Generate a JSON schema automatically: 25 26 ``` java 27 JSONSchema schema = SchemaGenerator.fromClass(User.class); 28 ``` 29 30 Supports: 31 32 - Records 33 - POJOs (getter-based) 34 - Nested objects 35 - Lists / Sets / Arrays 36 - Maps\<String, V\> 37 - Enums 38 - Optional`<T>` 39 - Validation annotations 40 41 ------------------------------------------------------------------------ 42 43 ## 3. Built-in JSON Parser 44 45 Oxyjen now includes a lightweight JSON parser: 46 47 ``` java 48 Object parsed = JsonParser.parse(jsonString); 49 ``` 50 51 Parses into: 52 53 | JSON | Java | 54 | --------| --------------------------------| 55 | object | Map\<String,Object\> | 56 | array | List`<Object>` | 57 | string | String | 58 | number | BigDecimal / Long / BigInteger | 59 | boolean | Boolean | 60 | null | null | 61 62 ------------------------------------------------------------------------ 63 64 ## 4. JSON -> Java Object Mapping 65 66 Convert validated JSON directly into Java types: 67 68 ``` java 69 User user = JsonMapper.deserialize(json, User.class); 70 ``` 71 72 Supports: 73 74 - Records 75 - POJOs with setters 76 - Nested objects 77 - Collections 78 - Arrays 79 - Maps 80 - Enums 81 - Optional fields 82 83 ------------------------------------------------------------------------ 84 85 ## 5. Schema Validation Engine 86 87 Validate model output against schema: 88 89 ``` java 90 SchemaValidator validator = new SchemaValidator(schema); 91 var result = validator.validate(json); 92 93 if (!result.isValid()) { 94 System.out.println(result.formatErrors()); 95 } 96 ``` 97 98 Detects: 99 100 - Missing required fields 101 - Unknown fields 102 - Type mismatches 103 - Pattern violations 104 - Enum mismatches 105 - Numeric constraints 106 - Nested errors 107 108 ------------------------------------------------------------------------ 109 110 ## 6. New SchemaNode`<T>`{=html} 111 112 SchemaNode combines: 113 114 - LLM call 115 - Retry loop 116 - Schema validation 117 - JSON parsing 118 - Java object mapping 119 120 ### Example 121 122 ``` java 123 SchemaNode<User> node = SchemaNode.builder(User.class) 124 .model("gpt-4o-mini") 125 .schema(SchemaGenerator.fromClass(User.class)) 126 .maxRetries(3) 127 .memory("chat") 128 .build(); 129 130 User user = node.process("Generate a user named Alice aged 30", context); 131 ``` 132 133 Result: 134 135 User[name=Alice, age=30] 136 137 ------------------------------------------------------------------------ 138 139 # Example Usage 140 141 ## Step 1 --- Define your Java type 142 143 ### Record example 144 145 ``` java 146 @Description("User profile") 147 public record User( 148 @Description("User name") 149 @Size(min = 1, max = 50) 150 String name, 151 152 @Min(0) 153 @Max(150) 154 int age 155 ) {} 156 ``` 157 158 ------------------------------------------------------------------------ 159 160 ## Step 2 --- Generate schema 161 162 ``` java 163 JSONSchema schema = SchemaGenerator.fromClass(User.class); 164 System.out.println(schema.toJSON()); 165 ``` 166 167 ------------------------------------------------------------------------ 168 169 ## Step 3 --- Build SchemaNode 170 171 ``` java 172 SchemaNode<User> node = SchemaNode.builder(User.class) 173 .model("gpt-4o-mini") 174 .schema(schema) 175 .maxRetries(3) 176 .build(); 177 ``` 178 179 ------------------------------------------------------------------------ 180 181 ## Step 4 --- Run 182 183 ``` java 184 User result = node.process( 185 "Create a JSON user with name Bob and age 42", 186 context 187 ); 188 ``` 189 190 ------------------------------------------------------------------------ 191 192 ## Step 5 --- Use typed result 193 194 ``` java 195 System.out.println(result.name()); 196 System.out.println(result.age()); 197 ``` 198 199 ------------------------------------------------------------------------ 200 201 # Supported Java Types 202 203 ## Primitives 204 205 - int 206 - long 207 - double 208 - float 209 - boolean 210 211 ## Wrappers 212 213 - Integer 214 - Long 215 - Double 216 - Boolean 217 218 ## Strings 219 220 - String 221 222 ## Enums 223 224 ``` java 225 enum Status { ACTIVE, INACTIVE } 226 ``` 227 228 ## Collections 229 230 - List`<T>` 231 - Set`<T>` 232 - T\[\] 233 234 ## Maps 235 236 - Map\<String, T\> 237 238 ## Optional 239 240 - Optional`<T>` 241 242 ## Nested Objects 243 244 ``` java 245 record Address(String city, String zip) {} 246 record User(String name, Address address) {} 247 ``` 248 249 ------------------------------------------------------------------------ 250 251 # Validation Annotations Supported 252 253 | Annotation | Effect | 254 | -------------| ---------------------------| 255 | @Description | Schema description | 256 | @Min | Numeric minimum | 257 | @Max | Numeric maximum | 258 | @Size | String length constraints | 259 | @Pattern | Regex validation | 260 | @JsonIgnore | Exclude field | 261 262 ------------------------------------------------------------------------- 263 264 ## Tools API 265 266 ## Overview 267 268 The Tools API enables: 269 270 - LLM-driven tool calling 271 - Graph-based execution via `ToolNode` 272 - Direct programmatic tool usage 273 - Built-in validation and safety 274 - Deterministic, observable execution 275 276 ------------------------------------------------------------------------ 277 278 ## Architecture 279 280 ToolCall → ToolNode → ToolExecutor → Tool → ToolResult 281 282 ------------------------------------------------------------------------ 283 284 # Public API Layer 285 286 ## Tool Interface 287 288 Tool is the core building block of the Tools API. It represents any executable unit that can be used by agents, graphs, or directly in code. 289 290 It is 291 - Decoupled from LLMs -> can be used independently or inside pipelines 292 - Self-describing -> defines input/output via JSON Schema 293 - Stateless -> no internal state (uses NodeContext) 294 - Safe & validated -> inputs are validated before execution 295 296 Core Methods 297 - name() -> unique identifier (used by LLMs) 298 - description() -> helps LLM decide when to use the tool 299 - inputSchema() -> defines expected inputs 300 - outputSchema() -> defines output structure 301 - execute() -> main logic of the tool 302 303 Optional Hooks 304 - isSafe() -> runtime safety checks 305 - estimateExecutionTime() -> planning & optimization 306 - timeoutMs() -> execution limits 307 - deterministic() -> indicates predictable output 308 309 ``` java 310 public interface Tool { 311 String name(); 312 String description(); 313 JSONSchema inputSchema(); 314 JSONSchema outputSchema(); 315 ToolResult execute(Map<String, Object> input, NodeContext context); 316 } 317 ``` 318 319 ------------------------------------------------------------------------ 320 321 ## ToolCall 322 323 ToolCall represents a request to execute a tool. It is typically generated by an LLM or constructed programmatically. 324 325 What it contains 326 - name -> which tool to call (must match Tool.name()) 327 - arguments -> input parameters for the tool 328 - id (optional) -> used for tracking in multi-tool workflows 329 330 Key Characteristics 331 - Immutable -> safe to pass across threads and pipelines 332 - LLM-friendly -> mirrors how LLMs generate tool calls 333 - Type-safe access -> getArgument(key, Class<T>) ensures correct types 334 - Builder support -> flexible construction 335 336 Core Methods 337 - getName() -> tool identifier 338 - getArguments() -> full argument map 339 - getArgument(key, type) -> typed access 340 - hasArgument(key) -> existence check 341 342 ``` java 343 ToolCall call = ToolCall.of("file_read", Map.of( 344 "path", "/tmp/test.txt", 345 "offset", 5 346 )); 347 348 ToolCall call = ToolCall.builder() 349 .name("file_read") 350 .argument("path", "/tmp/data.txt") 351 .build(); 352 ``` 353 354 ------------------------------------------------------------------------ 355 356 ## ToolResult 357 358 ToolResult represents the outcome of a tool execution. It is an immutable object that clearly separates success and failure cases. 359 360 What it contains: 361 - success / failure flag 362 - output → result data (on success) 363 - error → error message (on failure) 364 - metadata → extra info (timing, HTTP status, etc.) 365 - executionTimeMs → performance tracking 366 - timestamp → when execution happened 367 368 Key Features 369 - Fail-safe design → never ambiguous (either success or failure) 370 - Type-safe output access → getOutputAs(Class<T>) 371 - Safe accessors → Optional-based (getOutputSafe, getErrorSafe) 372 - Rich metadata → supports HTTP status, headers, custom data 373 - Builder pattern → flexible construction 374 - Validation enforced → prevents invalid states (e.g., success + error) 375 - 376 Core Methods 377 - isSuccess() / isFailure() 378 - getOutput() / getOutputAs() 379 - getError() 380 - getMetadata() 381 - getExecutionTimeMs() 382 - 383 ``` java 384 ToolResult result = ToolResult.success("calculator", 8); 385 ToolResult result = ToolResult.failure("file_read", "File not found"); 386 387 ToolResult result = executor.execute(call, context); 388 result.isSuccess(); 389 result.getOutput(); 390 result.getError(); 391 392 ToolResult.success() 393 ToolResult.failure() 394 ``` 395 396 ------------------------------------------------------------------------ 397 398 ## ToolExecutor 399 400 1. Tool Registry – Stores tools in an immutable map, prevents duplicates 401 2. Input Validation – Validates args using JSON schema (strict mode supported) 402 3. Permissions – Allows/block tools via ToolPermission 403 4. Safety Check – Runs tool.isSafe() before execution 404 5. Sandbox Execution – Enforces timeout & safe execution (sandbox.execute(() -> tool.execute(...))) 405 6. Output Serialization – Converts output to JSON-friendly format 406 7. Output Validation – Optional schema validation for outputs 407 8. Execution Tracking – Measures time + logs everything 408 9. Fail-safe Design – Never throws, always returns ToolResult 409 10. Lifecycle Hooks – Pre/post execution hooks via permissions(beforeExecution/afterExecution) 410 11. Builder Support – Flexible configuration 411 412 ```java 413 ToolExecutor executor = ToolExecutor.builder() 414 .addTool(new FileReaderTool(sandbox)) 415 .strictInputValidation(true) 416 .validateOutput(true) 417 .sandbox(sandbox) 418 .permission(permission) 419 .build(); 420 ``` 421 422 ------------------------------------------------------------------------ 423 424 ## ToolNode 425 426 ToolNode is a graph node wrapper around ToolExecutor. 427 428 1. Executes tools inside a graph 429 2. Delegates execution to ToolExecutor 430 3. Acts as a NodePlugin (fits into graph pipeline) 431 4. Supports multiple tools in one node 432 5. Provides lifecycle logging (onStart, onFinish, onError) 433 6. Exposes helper methods (hasTool, getToolNames) 434 435 ```java 436 ToolNode toolNode = new ToolNode( 437 new FileReaderTool(sandbox), 438 new HttpTool(...) 439 ); 440 441 Graph workflow = GraphBuilder.named("agent-pipeline") 442 .addNode(routerNode) 443 .addNode(toolNode) 444 .addNode(summaryNode) 445 .build(); 446 ``` 447 ------------------------------------------------------------------------- 448 449 ## Validation 450 451 - What it Validates? 452 1. Core Checks 453 2. ToolCall is not null 454 3. Tool is not null 455 4. Tool name matches (call.name == tool.name) 456 5. Arguments match JSON schema 457 6. Required fields are present 458 7. Types are correct 459 460 - Strict Mode (Important) 461 If strictMode = true: 462 Rejects unknown arguments 463 Prevents LLM hallucinated inputs 464 ToolValidator validator = new ToolValidator(true); 465 466 - Warnings 467 If no schema -> warning (not failure) 468 Non-critical issues are tracked separately 469 470 471 ```java 472 JSONSchema schema = JSONSchema.object() 473 .property("a", PropertySchema.number("first number").build()) 474 .property("b", PropertySchema.number("second number").build()) 475 .required("a", "b") 476 .build(); 477 478 ToolCall call = ToolCall.of("calculator", Map.of( 479 "a", 5, 480 "b", 3 481 )); 482 483 ToolValidator validator = new ToolValidator(true); 484 ValidationResult result = validator.validate(call, tool, context); 485 486 result.isValid(); 487 ``` 488 ------------------------------------------------------------------------ 489 490 # Safety Layer 491 492 ## ToolPermission 493 494 Basic interface to make your own permission systems 495 ``` java 496 // Controls WHO can use WHICH tools 497 if (!permission.isAllowed("file_delete", context)) { 498 return blocked; 499 } 500 ``` 501 502 ## AllowListPermission 503 504 Simple allowlist based permission system 505 ``` java 506 // Simple implementation: explicit allow list 507 AllowListPermission.allowOnly() 508 .allow("calculator") 509 .allow("web_search") 510 .build(); 511 ``` 512 513 ## ToolSandbox 514 515 Sandboxes tool execution to prevent escape and limit damage. Comes with two modes basic() and strict()(for strict validation). 516 ``` java 517 private Path tempDir = Files.createTempDirectory("filereader-test-"); 518 ToolSandbox sandbox = ToolSandbox.builder() 519 .allowedDirectory(tempDir.toString()) 520 .timeout(5, java.util.concurrent.TimeUnit.SECONDS) 521 .build(); 522 ``` 523 524 ------------------------------------------------------------------------ 525 526 # Built-in Tools 527 528 ## FileReaderTool 529 530 Overview 531 532 FileReaderTool is a secure, flexible file reading tool designed for safe access to local files with advanced capabilities like chunking, partial reads, and metadata extraction. 533 534 Key Features 535 Sandboxed file access 536 - Uses ToolSandbox to restrict file paths 537 - Prevents directory traversal & unauthorized access 538 539 Metadata support 540 - File size, MIME type, last modified timestamp 541 542 Partial reading 543 Read using: 544 - offset + limit (byte-based) 545 - lineStart + lineEnd (line-based) 546 547 Chunked reading 548 - Efficient for large files 549 - Supports chunkSize + chunkIndex 550 551 Caching 552 - Avoids repeated disk reads for unchanged files 553 554 Binary mode 555 - Returns Base64 encoded content for binary-safe handling 556 557 Size limits 558 - Prevents reading excessively large files 559 560 ```java 561 Tool fileTool = new FileReaderTool(sandbox); 562 563 ToolCall call = ToolCall.of("file_read", Map.of( 564 "path", "/tmp/data.txt", 565 "lineStart", 1, 566 "lineEnd", 10 567 )); 568 569 ToolResult result = executor.execute(call, context); 570 ``` 571 572 573 ## HttpTool 574 575 Overview 576 HttpTool is a safe HTTP client tool for making API calls and fetching web data with built-in security and limits. 577 578 Key Features 579 Supports HTTP methods 580 - GET, POST, PUT, DELETE, PATCH 581 582 Domain allowlist 583 - Restrict requests to trusted domains only 584 585 Timeout control 586 - Prevents hanging requests 587 588 Response size limiting 589 - Avoids large/unbounded payloads 590 591 Headers & query support 592 - Custom headers 593 - Query parameters 594 595 ```java 596 Tool httpTool = HttpTool.builder() 597 .allowDomain("api.github.com") 598 .timeout(5000) 599 .build(); 600 601 ToolCall call = ToolCall.of("http_request", Map.of( 602 "method", "GET", 603 "url", "https://api.github.com/users/octocat" 604 )); 605 606 ToolResult result = executor.execute(call, context); 607 ``` 608 609 ------------------------------------------------------------------------ 610 611 # 🧪 Example 612 613 ## HttpTool example - Create a github issue with HttpTool 614 ``` java 615 public static void main(String[]args) { 616 HttpTool httpTool = HttpTool.builder() 617 .allowDomain("api.github.com") 618 .timeout(5000) 619 .maxResponseSize(100000) 620 .build(); 621 String token = System.getenv("GITHUB_TOKEN"); 622 Map<String, Object> input = new HashMap<>(); 623 input.put("method", "POST"); 624 input.put("url", "https://api.github.com/repos/11divyansh/EventManagementSystemAPI/issues"); 625 input.put("headers", Map.of( 626 "Authorization", "Bearer " + token, 627 "Accept", "application/vnd.github+json", 628 "Content-Type", "application/json" 629 )); 630 631 input.put("body", """ 632 { 633 "title": "Test HttpTool issue creation", 634 "body": "Something broke in production" 635 } 636 """); 637 NodeContext context = new NodeContext(); 638 ToolResult result = httpTool.execute(input, context); 639 if (result.isSuccess()) { 640 System.out.println("Issue created!"); 641 out.println(result.getOutput()); 642 out.println(result); 643 out.println(result.getMetadata("location", String.class)); 644 } else { 645 out.println("Error: " + result.getError()); 646 result.getHttpStatus().ifPresent(status -> { 647 if (status == 404) { 648 out.println("Resource not found (fallback?)"); 649 } else if (status >= 500) { 650 out.println("Server error (retry?)"); 651 } 652 }); 653 result.getHttpError().ifPresent(err -> { 654 out.println("Error Type: " + err.get("_errorType")); 655 }); 656 result.getMetadata("headers", Map.class).ifPresent(headers -> { 657 out.println("Headers: " + headers); 658 }); 659 result.getHttpMessage().ifPresent(out::println); 660 } 661 } 662 ``` 663 664 ------------------------------------------------------------------------ 665 666 # Version 667 668 v0.4