/ docs / v0.4.md
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