README.md
   1  # DriftKit Workflows Examples Module
   2  
   3  ## Overview
   4  
   5  The `driftkit-workflows-examples` module provides comprehensive reference implementations demonstrating the capabilities of the new DriftKit workflow engine. It includes production-ready examples for chat systems, RAG (Retrieval-Augmented Generation), reasoning workflows, prompt engineering, and intelligent routing - all built on the `driftkit-workflow-engine-core` orchestration engine.
   6  
   7  ## Spring Boot Initialization
   8  
   9  To use the workflows examples module in your Spring Boot application:
  10  
  11  ```java
  12  @SpringBootApplication
  13  @ComponentScan(basePackages = {"ai.driftkit.workflows.examples"}) // Scan example workflows
  14  @EnableMongoRepositories(basePackages = "ai.driftkit.workflows.repository") // For persistence
  15  public class YourApplication {
  16      public static void main(String[] args) {
  17          SpringApplication.run(YourApplication.class, args);
  18      }
  19  }
  20  ```
  21  
  22  The module provides:
  23  - **Example workflows**: Ready-to-use workflow implementations in `ai.driftkit.workflows.examples.workflows`
  24  - **Service wrappers**: Spring services that wrap workflow execution
  25  - **Configuration**: Workflow instances configured via `EtlConfig`
  26  - **Dependencies**: Requires other DriftKit modules to be configured
  27  
  28  Example workflow registration:
  29  
  30  ```java
  31  @Configuration
  32  public class WorkflowConfig {
  33      
  34      @Bean
  35      public ReasoningWorkflow reasoningWorkflow(EtlConfig config, 
  36                                               PromptService promptService,
  37                                               ModelRequestService modelRequestService) throws IOException {
  38          return new ReasoningWorkflow(config, promptService, modelRequestService);
  39      }
  40      
  41      @Bean 
  42      public ChatWorkflow chatWorkflow(ModelClient modelClient,
  43                                     ModelRequestService modelRequestService,
  44                                     PromptService promptService) {
  45          return new ChatWorkflow(modelClient, modelRequestService, promptService);
  46      }
  47  }
  48  ```
  49  
  50  ## Architecture
  51  
  52  ### Module Structure
  53  
  54  ```
  55  driftkit-workflows-examples/
  56  ├── driftkit-workflows-examples-core/        # Core workflow implementations
  57  │   ├── domain/                              # Domain objects
  58  │   └── workflows/                           # Example workflow classes
  59  │       ├── ChatWorkflow.java                # Conversational AI with memory
  60  │       ├── RouterWorkflow.java              # Intelligent message routing
  61  │       ├── RAGModifyWorkflow.java           # Document ingestion for RAG
  62  │       ├── RAGSearchWorkflow.java           # Vector similarity search
  63  │       ├── ReasoningWorkflow.java           # Multi-step reasoning
  64  │       ├── EnhancedReasoningWorkflow.java   # Advanced reasoning with validation
  65  │       ├── PromptEngineerWorkflow.java      # Automated prompt engineering
  66  │       └── ModelWorkflow.java               # Base class for AI workflows
  67  ├── driftkit-workflows-examples-spring-boot-starter/ # Spring Boot integration
  68  │   └── service/                             # Spring service wrappers
  69  └── pom.xml                                  # Parent module configuration
  70  ```
  71  
  72  ### Key Dependencies
  73  
  74  The module integrates extensively with DriftKit components:
  75  
  76  - **driftkit-workflow-engine-core** - Core workflow orchestration engine with fluent builder API
  77  - **driftkit-workflow-engine-agents** - Multi-agent patterns and LLM agents
  78  - **driftkit-workflow-test-framework** - Testing utilities and mock builders
  79  - **DriftKit Clients** - AI model client abstractions
  80  - **DriftKit Vector** - Vector storage and similarity search
  81  - **DriftKit Embedding** - Text embedding generation
  82  - **DriftKit Context Engineering** - Prompt management
  83  - **DriftKit Common** - Shared utilities and domain objects
  84  
  85  ## Base Architecture
  86  
  87  ### ModelWorkflow Base Class
  88  
  89  All AI-powered workflows in these examples extend the `ModelWorkflow` base class (example implementation), which provides common patterns and utilities for AI interactions. Note that actual workflows should use the `driftkit-workflow-engine-core` fluent API:
  90  
  91  ```java
  92  public abstract class ModelWorkflow<I extends StartEvent, O> extends ExecutableWorkflow<I, O> {
  93      
  94      protected final ModelClient modelClient;
  95      protected final ModelRequestService modelRequestService;
  96      protected final PromptService promptService;
  97      
  98      // Core text-to-text operations
  99      protected ModelTextResponse sendTextToText(ModelRequestParams params, WorkflowContext context) throws Exception {
 100          if (StringUtils.isNotBlank(params.getPromptId())) {
 101              return executePromptById(params, context);
 102          } else {
 103              return executeCustomPrompt(params, context);
 104          }
 105      }
 106      
 107      // Text-to-image operations
 108      protected ModelImageResponse sendTextToImage(ModelRequestParams params, WorkflowContext context) throws Exception {
 109          ModelImageRequest request = ModelImageRequest.builder()
 110              .prompt(params.getPromptText())
 111              .quality(ModelImageRequest.Quality.standard)
 112              .n(1)
 113              .build();
 114              
 115          return modelClient.textToImage(request);
 116      }
 117      
 118      // Image-to-text operations
 119      protected ModelTextResponse sendImageToText(ModelRequestParams params, WorkflowContext context) throws Exception {
 120          List<ModelImageResponse.ModelContentMessage> messages = buildMultiModalMessages(params);
 121          
 122          ModelTextRequest request = ModelTextRequest.builder()
 123              .messages(messages)
 124              .temperature(params.getTemperature())
 125              .maxTokens(params.getMaxTokens())
 126              .build();
 127              
 128          return modelClient.imageToText(request);
 129      }
 130      
 131      // Helper for building multi-modal messages
 132      private List<ModelImageResponse.ModelContentMessage> buildMultiModalMessages(ModelRequestParams params) {
 133          List<ModelContentElement> content = new ArrayList<>();
 134          
 135          // Add text content
 136          if (StringUtils.isNotBlank(params.getPromptText())) {
 137              content.add(ModelContentElement.builder()
 138                  .type(ModelTextRequest.MessageType.text)
 139                  .text(params.getPromptText())
 140                  .build());
 141          }
 142          
 143          // Add image content
 144          if (CollectionUtils.isNotEmpty(params.getImageData())) {
 145              for (String imageData : params.getImageData()) {
 146                  content.add(ModelContentElement.builder()
 147                      .type(ModelTextRequest.MessageType.image)
 148                      .image(ModelContentElement.ImageData.builder()
 149                          .image(imageData.getBytes())
 150                          .mimeType("image/jpeg")
 151                          .build())
 152                      .build());
 153              }
 154          }
 155          
 156          return List.of(
 157              ModelImageResponse.ModelContentMessage.builder()
 158                  .role(Role.user)
 159                  .content(content)
 160                  .build()
 161          );
 162      }
 163  }
 164  ```
 165  
 166  ### ModelRequestParams Builder
 167  
 168  Fluent builder for configuring AI model requests:
 169  
 170  ```java
 171  @Data
 172  @Builder
 173  @NoArgsConstructor
 174  @AllArgsConstructor
 175  public class ModelRequestParams {
 176      private String promptId;
 177      private String promptText;
 178      private Map<String, Object> variables = new HashMap<>();
 179      private Double temperature;
 180      private String model;
 181      private List<ModelImageResponse.ModelContentMessage> contextMessages;
 182      private List<String> imageData;
 183      private Integer maxTokens;
 184      
 185      public static ModelRequestParams create() {
 186          return new ModelRequestParams();
 187      }
 188      
 189      public ModelRequestParams withVariable(String key, Object value) {
 190          if (this.variables == null) {
 191              this.variables = new HashMap<>();
 192          }
 193          this.variables.put(key, value);
 194          return this;
 195      }
 196      
 197      // Fluent setters for all properties
 198      public ModelRequestParams promptId(String promptId) {
 199          this.promptId = promptId;
 200          return this;
 201      }
 202      
 203      public ModelRequestParams promptText(String promptText) {
 204          this.promptText = promptText;
 205          return this;
 206      }
 207      
 208      public ModelRequestParams temperature(Double temperature) {
 209          this.temperature = temperature;
 210          return this;
 211      }
 212  }
 213  ```
 214  
 215  ## Workflow Examples
 216  
 217  ### 1. ChatWorkflow - Conversational AI with Memory
 218  
 219  Advanced conversational AI system with routing, memory management, and multi-modal capabilities:
 220  
 221  ```java
 222  @Component
 223  public class ChatWorkflow extends ModelWorkflow<ChatWorkflow.ChatStartEvent, ChatWorkflow.ChatResult> {
 224      
 225      private final LLMMemoryProvider memoryProvider;
 226      private final WorkflowRegistry workflowRegistry;
 227      
 228      @Step(name = "start")
 229      public ExternalEvent<RouterWorkflow.RouterStartEvent> start(ChatStartEvent startEvent, WorkflowContext workflowContext) throws Exception {
 230          MessageTask task = startEvent.getTask();
 231          
 232          // Check for image generation request
 233          if (task.getMessage().startsWith("image:")) {
 234              return handleImageGeneration(task, workflowContext);
 235          }
 236          
 237          // Route through RouterWorkflow for intent classification
 238          RouterWorkflow.RouterStartEvent routerEvent = new RouterWorkflow.RouterStartEvent();
 239          routerEvent.setMessage(task.getMessage());
 240          routerEvent.setChatId(task.getChatId());
 241          routerEvent.setRoutes(extractCustomRoutes(task));
 242          
 243          return new ExternalEvent<>(
 244              "router-workflow",
 245              routerEvent,
 246              "processResponse"
 247          );
 248      }
 249      
 250      @Step(name = "processResponse")
 251      public WorkflowEvent processResponse(DataEvent<RouterWorkflow.RouterResult> routerResult, WorkflowContext workflowContext) throws Exception {
 252          RouterWorkflow.RouterResult result = routerResult.getData();
 253          ChatStartEvent startEvent = workflowContext.get("startEvent", ChatStartEvent.class);
 254          MessageTask task = startEvent.getTask();
 255          
 256          // Handle different routing outcomes
 257          if (result.getRoutes().stream().anyMatch(r -> r.getValue() == RouterWorkflow.RouterDefaultOutputTypes.RAG)) {
 258              return handleRAGResponse(result, task, workflowContext);
 259          } else if (result.getRoutes().stream().anyMatch(r -> r.getValue() == RouterWorkflow.RouterDefaultOutputTypes.REASONING)) {
 260              return handleReasoningResponse(result, task, workflowContext);
 261          } else {
 262              return handleChatResponse(result, task, workflowContext);
 263          }
 264      }
 265      
 266      private WorkflowEvent handleRAGResponse(RouterWorkflow.RouterResult result, MessageTask task, WorkflowContext workflowContext) throws Exception {
 267          // Build context from retrieved documents
 268          StringBuilder context = new StringBuilder();
 269          for (DocumentsResult docs : result.getRelatedDocs()) {
 270              for (Document doc : docs.documents()) {
 271                  context.append(doc.getPageContent()).append("\n\n");
 272              }
 273          }
 274          
 275          // Create prompt with RAG context
 276          Map<String, Object> variables = new HashMap<>();
 277          variables.put("user_message", task.getMessage());
 278          variables.put("context", context.toString());
 279          variables.put("chat_history", getRecentChatHistory(task.getChatId()));
 280          
 281          ModelRequestParams params = ModelRequestParams.create()
 282              .promptId("rag-response")
 283              .withVariable("user_message", task.getMessage())
 284              .withVariable("context", context.toString())
 285              .temperature(0.7);
 286              
 287          ModelTextResponse response = sendTextToText(params, workflowContext);
 288          
 289          // Create result
 290          ChatResult chatResult = new ChatResult();
 291          chatResult.setRoute(result);
 292          chatResult.setResponse(response.getChoices().get(0).getMessage().getContent());
 293          
 294          return StopEvent.of(chatResult);
 295      }
 296      
 297      private WorkflowEvent handleImageGeneration(MessageTask task, WorkflowContext workflowContext) throws Exception {
 298          String prompt = task.getMessage().substring(6); // Remove "image:" prefix
 299          
 300          ModelRequestParams params = ModelRequestParams.create()
 301              .promptText(prompt)
 302              .temperature(0.8);
 303              
 304          ModelImageResponse response = sendTextToImage(params, workflowContext);
 305          
 306          // Save image and return reference
 307          String imageId = saveGeneratedImage(response.getBytes().get(0).getImage());
 308          
 309          ChatResult result = new ChatResult();
 310          result.setImageId(imageId);
 311          result.setResponse("Image generated successfully");
 312          
 313          return StopEvent.of(result);
 314      }
 315      
 316      // Data classes
 317      @Data
 318      @AllArgsConstructor
 319      @NoArgsConstructor
 320      public static class ChatStartEvent extends StartEvent {
 321          private MessageTask task;
 322          private int memoryLength = 10;
 323      }
 324      
 325      @Data
 326      @AllArgsConstructor
 327      @NoArgsConstructor
 328      public static class ChatResult {
 329          private RouterWorkflow.RouterResult route;
 330          private String response;
 331          private String imageId;
 332      }
 333  }
 334  ```
 335  
 336  ### 2. RouterWorkflow - Intelligent Message Routing
 337  
 338  Sophisticated intent classification and routing system:
 339  
 340  ```java
 341  @Component
 342  public class RouterWorkflow extends ModelWorkflow<RouterWorkflow.RouterStartEvent, RouterWorkflow.RouterResult> {
 343      
 344      private final VectorStore vectorStore;
 345      private final EmbeddingModel embeddingModel;
 346      
 347      @Step(name = "start")
 348      public DataEvent<String> start(RouterStartEvent startEvent, WorkflowContext workflowContext) throws Exception {
 349          String message = startEvent.getMessage();
 350          List<Route> customRoutes = startEvent.getRoutes();
 351          
 352          // Build routing prompt with available options
 353          Map<String, Object> variables = new HashMap<>();
 354          variables.put("user_message", message);
 355          variables.put("custom_routes", formatCustomRoutes(customRoutes));
 356          variables.put("default_input_types", Arrays.toString(RouterDefaultInputTypes.values()));
 357          variables.put("default_output_types", Arrays.toString(RouterDefaultOutputTypes.values()));
 358          
 359          ModelRequestParams params = ModelRequestParams.create()
 360              .promptId("router-classification")
 361              .variables(variables)
 362              .temperature(0.1); // Low temperature for consistent classification
 363              
 364          ModelTextResponse response = sendTextToText(params, workflowContext);
 365          String routingDecision = response.getChoices().get(0).getMessage().getContent();
 366          
 367          return DataEvent.of(routingDecision, "processRouting");
 368      }
 369      
 370      @Step(name = "processRouting")
 371      public DataEvent<RouterResult> processRouting(DataEvent<String> routingData, WorkflowContext workflowContext) throws Exception {
 372          String routingJson = routingData.getData();
 373          RouterStartEvent startEvent = workflowContext.get("startEvent", RouterStartEvent.class);
 374          
 375          // Parse routing decision
 376          ObjectMapper mapper = new ObjectMapper();
 377          JsonNode routingNode = mapper.readTree(routingJson);
 378          
 379          RouterResult result = new RouterResult();
 380          
 381          // Extract input type classifications
 382          JsonNode inputTypes = routingNode.get("input_types");
 383          if (inputTypes != null && inputTypes.isArray()) {
 384              Set<RouterDecision<RouterDefaultInputTypes>> inputDecisions = new HashSet<>();
 385              for (JsonNode inputType : inputTypes) {
 386                  RouterDecision<RouterDefaultInputTypes> decision = new RouterDecision<>();
 387                  decision.setValue(RouterDefaultInputTypes.valueOf(inputType.get("type").asText()));
 388                  decision.setConfidence(inputType.get("confidence").asDouble());
 389                  inputDecisions.add(decision);
 390              }
 391              result.setInputTypes(inputDecisions);
 392          }
 393          
 394          // Extract routing decisions
 395          JsonNode routes = routingNode.get("routes");
 396          if (routes != null && routes.isArray()) {
 397              Set<RouterDecision<RouterDefaultOutputTypes>> routeDecisions = new HashSet<>();
 398              for (JsonNode route : routes) {
 399                  RouterDecision<RouterDefaultOutputTypes> decision = new RouterDecision<>();
 400                  decision.setValue(RouterDefaultOutputTypes.valueOf(route.get("type").asText()));
 401                  decision.setConfidence(route.get("confidence").asDouble());
 402                  routeDecisions.add(decision);
 403              }
 404              result.setRoutes(routeDecisions);
 405          }
 406          
 407          // If RAG routing is detected, perform document retrieval
 408          boolean needsRAG = result.getRoutes().stream()
 409              .anyMatch(r -> r.getValue() == RouterDefaultOutputTypes.RAG);
 410              
 411          if (needsRAG) {
 412              return DataEvent.of(result, "performRAGSearch");
 413          } else {
 414              return DataEvent.of(result, "finalizeResult");
 415          }
 416      }
 417      
 418      @Step(name = "performRAGSearch")
 419      public DataEvent<RouterResult> performRAGSearch(DataEvent<RouterResult> routerData, WorkflowContext workflowContext) throws Exception {
 420          RouterResult result = routerData.getData();
 421          RouterStartEvent startEvent = workflowContext.get("startEvent", RouterStartEvent.class);
 422          
 423          // Generate embedding for the user message
 424          TextSegment segment = TextSegment.from(startEvent.getMessage());
 425          Response<Embedding> embeddingResponse = embeddingModel.embed(segment);
 426          
 427          // Search for relevant documents across multiple indexes
 428          List<DocumentsResult> allResults = new ArrayList<>();
 429          
 430          // Search default indexes
 431          for (String index : getDefaultIndexes()) {
 432              try {
 433                  DocumentsResult docs = vectorStore.findRelevant(
 434                      index, 
 435                      embeddingResponse.content().vector(), 
 436                      5
 437                  );
 438                  if (!docs.isEmpty()) {
 439                      allResults.add(docs);
 440                  }
 441              } catch (Exception e) {
 442                  log.warn("Failed to search index: {}", index, e);
 443              }
 444          }
 445          
 446          result.setRelatedDocs(allResults);
 447          
 448          return DataEvent.of(result, "finalizeResult");
 449      }
 450      
 451      @Step(name = "finalizeResult")
 452      public StopEvent<RouterResult> finalizeResult(DataEvent<RouterResult> routerData, WorkflowContext workflowContext) {
 453          RouterResult result = routerData.getData();
 454          
 455          // Apply confidence thresholds and quality checks
 456          result.setRoutes(filterByConfidence(result.getRoutes(), 0.6));
 457          result.setInputTypes(filterByConfidence(result.getInputTypes(), 0.7));
 458          
 459          return StopEvent.of(result);
 460      }
 461      
 462      // Enums and data classes
 463      public enum RouterDefaultInputTypes {
 464          GREETING, INFORMATION_REQUEST, CLARIFICATION, CHAT,
 465          IMAGE_GENERATION, FEEDBACK, ESCALATION, SALES_SUPPORT,
 466          PRODUCT_ISSUE, CUSTOM, UNKNOWN
 467      }
 468      
 469      public enum RouterDefaultOutputTypes {
 470          RAG, SUPPORT_REQUEST, REDO_WITH_SMARTER_MODEL,
 471          REASONING, SALES_REQUEST, CHAT
 472      }
 473      
 474      @Data
 475      @AllArgsConstructor
 476      @NoArgsConstructor
 477      public static class RouterStartEvent extends StartEvent {
 478          private String message;
 479          private String chatId;
 480          private List<Route> routes = new ArrayList<>();
 481      }
 482      
 483      @Data
 484      @AllArgsConstructor
 485      @NoArgsConstructor
 486      public static class RouterResult {
 487          private Set<RouterDecision<RouterDefaultInputTypes>> inputTypes = new HashSet<>();
 488          private Set<RouterDecision<RouterDefaultOutputTypes>> routes = new HashSet<>();
 489          private Set<RouterDecision<String>> customRoutes = new HashSet<>();
 490          private Set<RouterDecision<String>> indexes = new HashSet<>();
 491          private List<DocumentsResult> relatedDocs = new ArrayList<>();
 492      }
 493      
 494      @Data
 495      @AllArgsConstructor
 496      @NoArgsConstructor
 497      public static class RouterDecision<T> {
 498          private T value;
 499          private double confidence;
 500          private String reasoning;
 501      }
 502  }
 503  ```
 504  
 505  ### 3. RAGModifyWorkflow - Document Ingestion
 506  
 507  Comprehensive document processing and vector storage workflow:
 508  
 509  ```java
 510  @Component
 511  public class RAGModifyWorkflow extends ModelWorkflow<RAGModifyWorkflow.DocumentsEvent, RAGModifyWorkflow.DocumentSaveResult> {
 512      
 513      private final UnifiedParser unifiedParser;
 514      private final VectorStore vectorStore;
 515      private final EmbeddingModel embeddingModel;
 516      private final DocumentSplitter documentSplitter;
 517      private final ThreadPoolExecutor executor;
 518      
 519      public RAGModifyWorkflow() {
 520          this.documentSplitter = DocumentSplitter.builder()
 521              .maxChunkSize(512)
 522              .overlapSize(50)
 523              .tokenizer(new SimpleTokenizer())
 524              .build();
 525              
 526          this.executor = new ThreadPoolExecutor(
 527              4, 8, 60L, TimeUnit.SECONDS,
 528              new LinkedBlockingQueue<>(100)
 529          );
 530      }
 531      
 532      @Step
 533      public DataEvent<ParsedContent> parseInput(DocumentsEvent startEvent, WorkflowContext workflowContext) throws Exception {
 534          ParserInput input = createParserInput(startEvent);
 535          ParsedContent parsed = unifiedParser.parse(input);
 536          
 537          // Enhance metadata
 538          Map<String, Object> metadata = new HashMap<>(parsed.getMetadata());
 539          metadata.put("index_id", startEvent.getIndexId());
 540          metadata.put("source_type", startEvent.getSourceType());
 541          metadata.put("processing_time", System.currentTimeMillis());
 542          
 543          ParsedContent enriched = ParsedContent.builder()
 544              .content(parsed.getContent())
 545              .contentType(parsed.getContentType())
 546              .metadata(metadata)
 547              .build();
 548              
 549          return DataEvent.of(enriched);
 550      }
 551      
 552      @Step
 553      public StopEvent<DocumentSaveResult> ingestDocument(DataEvent<ParsedContent> documentEvent, WorkflowContext workflowContext) throws Exception {
 554          ParsedContent parsed = documentEvent.getData();
 555          DocumentsEvent startEvent = workflowContext.get("startEvent", DocumentsEvent.class);
 556          
 557          // Split content into chunks
 558          List<String> chunks = documentSplitter.split(parsed.getContent());
 559          
 560          // Process chunks concurrently
 561          List<CompletableFuture<Document>> futures = new ArrayList<>();
 562          
 563          for (int i = 0; i < chunks.size(); i++) {
 564              final int chunkIndex = i;
 565              final String chunk = chunks.get(i);
 566              
 567              CompletableFuture<Document> future = CompletableFuture.supplyAsync(() -> {
 568                  try {
 569                      // Generate embedding
 570                      TextSegment segment = TextSegment.from(chunk);
 571                      Response<Embedding> embeddingResponse = embeddingModel.embed(segment);
 572                      
 573                      // Create document
 574                      Document document = new Document();
 575                      document.setId(UUID.randomUUID().toString());
 576                      document.setPageContent(chunk);
 577                      document.setVector(embeddingResponse.content().vector());
 578                      
 579                      // Add chunk metadata
 580                      Map<String, Object> chunkMetadata = new HashMap<>(parsed.getMetadata());
 581                      chunkMetadata.put("chunk_index", chunkIndex);
 582                      chunkMetadata.put("total_chunks", chunks.size());
 583                      chunkMetadata.put("chunk_size", chunk.length());
 584                      
 585                      document.setMetadata(chunkMetadata);
 586                      
 587                      return document;
 588                  } catch (Exception e) {
 589                      throw new RuntimeException("Failed to process chunk " + chunkIndex, e);
 590                  }
 591              }, executor);
 592              
 593              futures.add(future);
 594          }
 595          
 596          // Wait for all chunks to be processed
 597          List<Document> documents = futures.stream()
 598              .map(CompletableFuture::join)
 599              .collect(Collectors.toList());
 600          
 601          // Store documents in vector database
 602          List<String> documentIds = vectorStore.addDocuments(startEvent.getIndexId(), documents);
 603          
 604          // Create result
 605          DocumentSaveResult result = DocumentSaveResult.builder()
 606              .documentIds(documentIds)
 607              .totalDocuments(documents.size())
 608              .indexId(startEvent.getIndexId())
 609              .contentType(parsed.getContentType())
 610              .originalSize(parsed.getContent().length())
 611              .totalChunks(chunks.size())
 612              .build();
 613              
 614          return StopEvent.of(result);
 615      }
 616      
 617      private ParserInput createParserInput(DocumentsEvent event) {
 618          switch (event.getSourceType()) {
 619              case "youtube":
 620                  return YoutubeIdParserInput.builder()
 621                      .youtubeId(event.getYoutubeId())
 622                      .languages(event.getLanguages())
 623                      .build();
 624                      
 625              case "file":
 626                  return ByteArrayParserInput.builder()
 627                      .data(event.getFileData())
 628                      .contentType(ContentType.fromMimeType(event.getMimeType()))
 629                      .metadata(event.getMetadata())
 630                      .build();
 631                      
 632              case "text":
 633              default:
 634                  return StringParserInput.builder()
 635                      .content(event.getTextContent())
 636                      .metadata(event.getMetadata())
 637                      .build();
 638          }
 639      }
 640      
 641      // Data classes
 642      @Data
 643      @Builder
 644      @NoArgsConstructor
 645      @AllArgsConstructor
 646      public static class DocumentsEvent extends StartEvent {
 647          private String indexId;
 648          private String sourceType; // "youtube", "file", "text"
 649          
 650          // YouTube specific
 651          private String youtubeId;
 652          private List<String> languages;
 653          
 654          // File specific
 655          private byte[] fileData;
 656          private String mimeType;
 657          
 658          // Text specific
 659          private String textContent;
 660          
 661          // Common metadata
 662          private Map<String, Object> metadata = new HashMap<>();
 663      }
 664      
 665      @Data
 666      @Builder
 667      @NoArgsConstructor
 668      @AllArgsConstructor
 669      public static class DocumentSaveResult {
 670          private List<String> documentIds;
 671          private int totalDocuments;
 672          private String indexId;
 673          private ContentType contentType;
 674          private int originalSize;
 675          private int totalChunks;
 676          private long processingTimeMs;
 677      }
 678  }
 679  ```
 680  
 681  ### 4. EnhancedReasoningWorkflow - Advanced Reasoning with Quality Control
 682  
 683  Sophisticated reasoning system with planning, validation, and quality control:
 684  
 685  ```java
 686  @Component
 687  public class EnhancedReasoningWorkflow extends ModelWorkflow<StartEvent, EnhancedReasoningWorkflow.EnhancedReasoningResult> {
 688      
 689      private static final double SATISFACTION_THRESHOLD = 0.8;
 690      private static final int MAX_RETRY_ATTEMPTS = 3;
 691      private static final double MIN_RESULT_LENGTH_RATIO = 0.5;
 692      
 693      private final ChecklistService checklistService;
 694      private final WorkflowRegistry workflowRegistry;
 695      
 696      @Step(name = "start")
 697      public DataEvent<EnhancedReasoningPlan> start(StartEvent startEvent, WorkflowContext workflowContext) throws Exception {
 698          String task = startEvent.getData();
 699          
 700          // Generate reasoning plan with checklist
 701          Map<String, Object> variables = new HashMap<>();
 702          variables.put("task", task);
 703          
 704          ModelRequestParams params = ModelRequestParams.create()
 705              .promptId("enhanced-reasoning-plan")
 706              .variables(variables)
 707              .temperature(0.3);
 708              
 709          ModelTextResponse response = sendTextToText(params, workflowContext);
 710          String planJson = response.getChoices().get(0).getMessage().getContent();
 711          
 712          // Parse plan
 713          ObjectMapper mapper = new ObjectMapper();
 714          JsonNode planNode = mapper.readTree(planJson);
 715          
 716          EnhancedReasoningPlan plan = new EnhancedReasoningPlan();
 717          plan.setPlan(planNode.get("plan").asText());
 718          plan.setPlanConfidence(planNode.get("confidence").asDouble());
 719          plan.setTask(task);
 720          plan.setAttemptCount(1);
 721          
 722          // Extract and save checklist items
 723          JsonNode checklistNode = planNode.get("checklist");
 724          if (checklistNode != null && checklistNode.isArray()) {
 725              String checklistId = UUID.randomUUID().toString();
 726              plan.setChecklistId(checklistId);
 727              
 728              for (JsonNode item : checklistNode) {
 729                  checklistService.addChecklistItem(checklistId, item.asText());
 730              }
 731          }
 732          
 733          return DataEvent.of(plan, "executeStep");
 734      }
 735      
 736      @Step(name = "executeStep", invocationLimit = 5)
 737      public DataEvent<EnhancedReasoningExecution> executeStep(DataEvent<EnhancedReasoningPlan> planEvent, WorkflowContext workflowContext) throws Exception {
 738          EnhancedReasoningPlan plan = planEvent.getData();
 739          
 740          // Build execution context
 741          Map<String, Object> variables = new HashMap<>();
 742          variables.put("task", plan.getTask());
 743          variables.put("plan", plan.getPlan());
 744          
 745          // Add checklist items if available
 746          if (plan.getChecklistId() != null) {
 747              List<ChecklistItemEntity> checklistItems = checklistService.getChecklistItems(plan.getChecklistId());
 748              List<String> checklist = checklistItems.stream()
 749                  .map(ChecklistItemEntity::getContent)
 750                  .collect(Collectors.toList());
 751              variables.put("checklist", checklist);
 752          }
 753          
 754          ModelRequestParams params = ModelRequestParams.create()
 755              .promptId("enhanced-reasoning-execution")
 756              .variables(variables)
 757              .temperature(0.7);
 758              
 759          ModelTextResponse response = sendTextToText(params, workflowContext);
 760          String executionJson = response.getChoices().get(0).getMessage().getContent();
 761          
 762          // Parse execution result
 763          ObjectMapper mapper = new ObjectMapper();
 764          JsonNode executionNode = mapper.readTree(executionJson);
 765          
 766          EnhancedReasoningExecution execution = new EnhancedReasoningExecution();
 767          execution.setResult(executionNode.get("result").asText());
 768          execution.setResultConfidence(executionNode.get("confidence").asDouble());
 769          execution.setChecklist(plan.getChecklistId());
 770          execution.setPlan(plan);
 771          
 772          return DataEvent.of(execution, "validateStep");
 773      }
 774      
 775      @Step(name = "validateStep", invocationLimit = 3)
 776      public WorkflowEvent validateStep(DataEvent<EnhancedReasoningExecution> executionEvent, WorkflowContext workflowContext) throws Exception {
 777          EnhancedReasoningExecution execution = executionEvent.getData();
 778          
 779          // Validate result quality
 780          Map<String, Object> variables = new HashMap<>();
 781          variables.put("task", execution.getPlan().getTask());
 782          variables.put("plan", execution.getPlan().getPlan());
 783          variables.put("result", execution.getResult());
 784          
 785          ModelRequestParams params = ModelRequestParams.create()
 786              .promptId("enhanced-reasoning-validation")
 787              .variables(variables)
 788              .temperature(0.2);
 789              
 790          ModelTextResponse response = sendTextToText(params, workflowContext);
 791          String validationJson = response.getChoices().get(0).getMessage().getContent();
 792          
 793          // Parse validation result
 794          ObjectMapper mapper = new ObjectMapper();
 795          JsonNode validationNode = mapper.readTree(validationJson);
 796          
 797          EnhancedReasoningValidation validation = new EnhancedReasoningValidation();
 798          validation.setValidation(validationNode.get("validation").asText());
 799          validation.setSatisfied(validationNode.get("satisfied").asBoolean());
 800          validation.setSatisfactionScore(validationNode.get("satisfaction_score").asDouble());
 801          validation.setExecution(execution);
 802          
 803          // Check quality thresholds
 804          boolean passesQualityCheck = validation.getSatisfactionScore() >= SATISFACTION_THRESHOLD &&
 805                                     validation.isSatisfied() &&
 806                                     execution.getResult().length() >= (execution.getPlan().getTask().length() * MIN_RESULT_LENGTH_RATIO);
 807          
 808          if (passesQualityCheck) {
 809              // Success - create final result
 810              EnhancedReasoningResult finalResult = EnhancedReasoningResult.builder()
 811                  .plan(execution.getPlan().getPlan())
 812                  .planValidation("Plan executed successfully")
 813                  .planConfidence(execution.getPlan().getPlanConfidence())
 814                  .result(execution.getResult())
 815                  .resultValidation(validation.getValidation())
 816                  .resultConfidence(execution.getResultConfidence())
 817                  .isSatisfied(true)
 818                  .attemptHistory(List.of("Attempt " + execution.getPlan().getAttemptCount() + ": Success"))
 819                  .build();
 820                  
 821              return StopEvent.of(finalResult);
 822          } else if (execution.getPlan().getAttemptCount() < MAX_RETRY_ATTEMPTS) {
 823              // Retry with improvements
 824              return DataEvent.of(validation, "retryStep");
 825          } else {
 826              // Fallback to original ReasoningWorkflow
 827              return DataEvent.of(validation, "fallbackStep");
 828          }
 829      }
 830      
 831      @Step(name = "retryStep")
 832      public WorkflowEvent retryStep(DataEvent<EnhancedReasoningValidation> validationEvent, WorkflowContext workflowContext) throws Exception {
 833          EnhancedReasoningValidation validation = validationEvent.getData();
 834          EnhancedReasoningPlan originalPlan = validation.getExecution().getPlan();
 835          
 836          // Improve plan based on validation feedback
 837          Map<String, Object> variables = new HashMap<>();
 838          variables.put("original_task", originalPlan.getTask());
 839          variables.put("original_plan", originalPlan.getPlan());
 840          variables.put("previous_result", validation.getExecution().getResult());
 841          variables.put("validation_feedback", validation.getValidation());
 842          
 843          ModelRequestParams params = ModelRequestParams.create()
 844              .promptId("enhanced-reasoning-improvement")
 845              .variables(variables)
 846              .temperature(0.4);
 847              
 848          ModelTextResponse response = sendTextToText(params, workflowContext);
 849          String improvedPlanJson = response.getChoices().get(0).getMessage().getContent();
 850          
 851          // Parse improved plan
 852          ObjectMapper mapper = new ObjectMapper();
 853          JsonNode planNode = mapper.readTree(improvedPlanJson);
 854          
 855          EnhancedReasoningPlan improvedPlan = new EnhancedReasoningPlan();
 856          improvedPlan.setPlan(planNode.get("improved_plan").asText());
 857          improvedPlan.setPlanConfidence(planNode.get("confidence").asDouble());
 858          improvedPlan.setTask(originalPlan.getTask());
 859          improvedPlan.setAttemptCount(originalPlan.getAttemptCount() + 1);
 860          improvedPlan.setChecklistId(originalPlan.getChecklistId());
 861          
 862          return DataEvent.of(improvedPlan, "executeStep");
 863      }
 864      
 865      @Step(name = "fallbackStep")
 866      public WorkflowEvent fallbackStep(DataEvent<EnhancedReasoningValidation> validationEvent, WorkflowContext workflowContext) throws Exception {
 867          EnhancedReasoningValidation validation = validationEvent.getData();
 868          String task = validation.getExecution().getPlan().getTask();
 869          
 870          // Fallback to original ReasoningWorkflow
 871          StartEvent fallbackEvent = new StartEvent() {
 872              @Override
 873              public String getData() {
 874                  return task;
 875              }
 876          };
 877          
 878          ExternalEvent<StartEvent> externalEvent = new ExternalEvent<>(
 879              "reasoning-workflow",
 880              fallbackEvent,
 881              "finalizeFallback"
 882          );
 883          
 884          return externalEvent;
 885      }
 886      
 887      @FinalStep(name = "finalizeFallback")
 888      public StopEvent<EnhancedReasoningResult> finalizeFallback(DataEvent<String> fallbackResult, WorkflowContext workflowContext) {
 889          String result = fallbackResult.getData();
 890          
 891          EnhancedReasoningResult finalResult = EnhancedReasoningResult.builder()
 892              .plan("Fallback to standard reasoning")
 893              .planValidation("Enhanced reasoning failed, used fallback")
 894              .planConfidence(0.6)
 895              .result(result)
 896              .resultValidation("Fallback result")
 897              .resultConfidence(0.7)
 898              .isSatisfied(false)
 899              .fallbackWorkflowId("reasoning-workflow")
 900              .attemptHistory(List.of("Enhanced reasoning failed, used fallback workflow"))
 901              .build();
 902              
 903          return StopEvent.of(finalResult);
 904      }
 905      
 906      // Data classes
 907      @Data
 908      @Builder
 909      @NoArgsConstructor
 910      @AllArgsConstructor
 911      public static class EnhancedReasoningPlan {
 912          private String plan;
 913          private String task;
 914          private double planConfidence;
 915          private String checklistId;
 916          private int attemptCount;
 917      }
 918      
 919      @Data
 920      @Builder
 921      @NoArgsConstructor
 922      @AllArgsConstructor
 923      public static class EnhancedReasoningExecution {
 924          private String result;
 925          private double resultConfidence;
 926          private String checklist;
 927          private EnhancedReasoningPlan plan;
 928      }
 929      
 930      @Data
 931      @Builder
 932      @NoArgsConstructor
 933      @AllArgsConstructor
 934      public static class EnhancedReasoningValidation {
 935          private String validation;
 936          private boolean satisfied;
 937          private double satisfactionScore;
 938          private EnhancedReasoningExecution execution;
 939      }
 940      
 941      @Data
 942      @Builder
 943      @NoArgsConstructor
 944      @AllArgsConstructor
 945      public static class EnhancedReasoningResult {
 946          private String plan;
 947          private String planValidation;
 948          private Double planConfidence;
 949          private String result;
 950          private String resultValidation;
 951          private Double resultConfidence;
 952          private Boolean isSatisfied;
 953          private String fallbackWorkflowId;
 954          private List<String> attemptHistory;
 955          private Map<String, Object> metadata;
 956      }
 957  }
 958  ```
 959  
 960  ### 5. PromptEngineerWorkflow - Automated Prompt Engineering
 961  
 962  Intelligent prompt creation, editing, and testing system:
 963  
 964  ```java
 965  @Component
 966  public class PromptEngineerWorkflow extends ModelWorkflow<StartEvent, PromptEngineerWorkflow.PromptEngineerResult> {
 967      
 968      private final WorkflowRegistry workflowRegistry;
 969      private final VariableExtractor variableExtractor;
 970      
 971      @Step(name = "start")
 972      public DataEvent<PromptParameters> start(StartEvent startEvent, WorkflowContext workflowContext) throws Exception {
 973          String userRequest = startEvent.getData();
 974          
 975          // Extract parameters from user request
 976          Map<String, Object> variables = new HashMap<>();
 977          variables.put("user_request", userRequest);
 978          
 979          ModelRequestParams params = ModelRequestParams.create()
 980              .promptId("prompt-parameter-extraction")
 981              .variables(variables)
 982              .temperature(0.2);
 983              
 984          ModelTextResponse response = sendTextToText(params, workflowContext);
 985          String parametersJson = response.getChoices().get(0).getMessage().getContent();
 986          
 987          // Parse parameters
 988          ObjectMapper mapper = new ObjectMapper();
 989          JsonNode parametersNode = mapper.readTree(parametersJson);
 990          
 991          PromptParameters promptParams = new PromptParameters();
 992          promptParams.setTask(parametersNode.get("task").asText());
 993          promptParams.setPromptId(parametersNode.path("prompt_id").asText(null));
 994          promptParams.setWorkflowId(parametersNode.path("workflow_id").asText(null));
 995          promptParams.setLanguage(Language.valueOf(
 996              parametersNode.path("language").asText("GENERAL").toUpperCase()
 997          ));
 998          
 999          // Parse additional parameters
1000          JsonNode paramsNode = parametersNode.get("parameters");
1001          if (paramsNode != null) {
1002              Map<String, Object> additionalParams = mapper.convertValue(paramsNode, Map.class);
1003              promptParams.setParameters(additionalParams);
1004          }
1005          
1006          return DataEvent.of(promptParams, "engineerPrompt");
1007      }
1008      
1009      @Step(name = "engineerPrompt")
1010      public DataEvent<String> engineerPrompt(DataEvent<PromptParameters> parametersEvent, WorkflowContext workflowContext) throws Exception {
1011          PromptParameters promptParams = parametersEvent.getData();
1012          
1013          String promptAction = promptParams.getPromptId() != null ? "edit_prompt" : "create_prompt";
1014          
1015          // Build context for prompt engineering
1016          Map<String, Object> variables = new HashMap<>();
1017          variables.put("task", promptParams.getTask());
1018          variables.put("action", promptAction);
1019          variables.put("language", promptParams.getLanguage().toString());
1020          
1021          if (promptParams.getPromptId() != null) {
1022              // Get existing prompt for editing
1023              Optional<Prompt> existingPrompt = promptService.getPromptById(promptParams.getPromptId());
1024              if (existingPrompt.isPresent()) {
1025                  variables.put("existing_prompt", existingPrompt.get().getMessage());
1026                  variables.put("existing_system_message", existingPrompt.get().getSystemMessage());
1027              }
1028          }
1029          
1030          if (promptParams.getParameters() != null) {
1031              variables.putAll(promptParams.getParameters());
1032          }
1033          
1034          // Use EnhancedReasoningWorkflow for sophisticated prompt engineering
1035          StartEvent reasoningEvent = new StartEvent() {
1036              @Override
1037              public String getData() {
1038                  return String.format(
1039                      "Engineer a high-quality prompt for the following task: %s. " +
1040                      "Action: %s. Language: %s. Consider best practices for prompt engineering.",
1041                      promptParams.getTask(), promptAction, promptParams.getLanguage()
1042                  );
1043              }
1044          };
1045          
1046          ExternalEvent<StartEvent> externalEvent = new ExternalEvent<>(
1047              "enhanced-reasoning-workflow",
1048              reasoningEvent,
1049              "processEngineeredPrompt"
1050          );
1051          
1052          return DataEvent.of(externalEvent, "processEngineeredPrompt");
1053      }
1054      
1055      @Step(name = "processEngineeredPrompt")
1056      public DataEvent<String> processEngineeredPrompt(DataEvent<EnhancedReasoningWorkflow.EnhancedReasoningResult> reasoningResult, WorkflowContext workflowContext) throws Exception {
1057          EnhancedReasoningWorkflow.EnhancedReasoningResult result = reasoningResult.getData();
1058          String engineeredPrompt = result.getResult();
1059          
1060          // Extract variables from the engineered prompt
1061          Set<String> variables = variableExtractor.extractVariables(engineeredPrompt);
1062          
1063          // Parse the engineered prompt if it's structured
1064          String finalPrompt = engineeredPrompt;
1065          String systemMessage = "";
1066          
1067          try {
1068              ObjectMapper mapper = new ObjectMapper();
1069              JsonNode promptNode = mapper.readTree(engineeredPrompt);
1070              
1071              if (promptNode.has("prompt")) {
1072                  finalPrompt = promptNode.get("prompt").asText();
1073              }
1074              if (promptNode.has("system_message")) {
1075                  systemMessage = promptNode.get("system_message").asText();
1076              }
1077          } catch (Exception e) {
1078              // Not JSON, use as-is
1079          }
1080          
1081          // Create or update prompt
1082          PromptParameters promptParams = workflowContext.get("promptParameters", PromptParameters.class);
1083          
1084          Prompt prompt = new Prompt();
1085          if (promptParams.getPromptId() != null) {
1086              // Update existing prompt
1087              Optional<Prompt> existing = promptService.getPromptById(promptParams.getPromptId());
1088              if (existing.isPresent()) {
1089                  prompt = existing.get();
1090              }
1091          } else {
1092              // Create new prompt
1093              prompt.setId(UUID.randomUUID().toString());
1094              prompt.setMethod("generated-" + UUID.randomUUID().toString().substring(0, 8));
1095              prompt.setCreatedTime(System.currentTimeMillis());
1096              prompt.setState(Prompt.State.MODERATION);
1097          }
1098          
1099          prompt.setMessage(finalPrompt);
1100          prompt.setSystemMessage(systemMessage);
1101          prompt.setLanguage(promptParams.getLanguage());
1102          prompt.setUpdatedTime(System.currentTimeMillis());
1103          
1104          // Save prompt
1105          Prompt savedPrompt = promptService.savePrompt(prompt);
1106          
1107          return DataEvent.of(savedPrompt.getId(), "testPrompt");
1108      }
1109      
1110      @Step(name = "testPrompt")
1111      public StopEvent<PromptEngineerResult> testPrompt(DataEvent<String> promptIdEvent, WorkflowContext workflowContext) throws Exception {
1112          String promptId = promptIdEvent.getData();
1113          PromptParameters promptParams = workflowContext.get("promptParameters", PromptParameters.class);
1114          
1115          // Generate test variables based on the prompt
1116          Optional<Prompt> promptOpt = promptService.getPromptById(promptId);
1117          if (promptOpt.isEmpty()) {
1118              throw new IllegalStateException("Prompt not found: " + promptId);
1119          }
1120          
1121          Prompt prompt = promptOpt.get();
1122          Set<String> variables = variableExtractor.extractVariables(prompt.getMessage());
1123          
1124          // Auto-generate test values
1125          Map<String, Object> testVariables = new HashMap<>();
1126          for (String variable : variables) {
1127              testVariables.put(variable, generateTestValue(variable));
1128          }
1129          
1130          Object testResult = null;
1131          
1132          // Test the prompt
1133          if (promptParams.getWorkflowId() != null && workflowRegistry.hasWorkflow(promptParams.getWorkflowId())) {
1134              // Test with specific workflow
1135              testResult = testWithWorkflow(promptParams.getWorkflowId(), promptId, testVariables);
1136          } else {
1137              // Test with direct prompt execution
1138              testResult = testWithDirectExecution(promptId, testVariables);
1139          }
1140          
1141          // Create result
1142          PromptEngineerResult result = new PromptEngineerResult();
1143          result.setPrompt(prompt.getMessage());
1144          result.setTestResult(testResult);
1145          
1146          return StopEvent.of(result);
1147      }
1148      
1149      private Object testWithWorkflow(String workflowId, String promptId, Map<String, Object> variables) throws Exception {
1150          // Create appropriate start event for the workflow
1151          StartEvent startEvent = createWorkflowStartEvent(workflowId, promptId, variables);
1152          
1153          // Execute workflow
1154          StopEvent<?> result = workflowRegistry.executeWorkflow(workflowId, startEvent, new WorkflowContext());
1155          
1156          return result.getResult();
1157      }
1158      
1159      private Object testWithDirectExecution(String promptId, Map<String, Object> variables) throws Exception {
1160          ModelRequestParams params = ModelRequestParams.create()
1161              .promptId(promptId)
1162              .variables(variables)
1163              .temperature(0.7);
1164              
1165          ModelTextResponse response = sendTextToText(params, new WorkflowContext());
1166          return response.getChoices().get(0).getMessage().getContent();
1167      }
1168      
1169      private String generateTestValue(String variableName) {
1170          // Simple heuristic-based test value generation
1171          String lowerName = variableName.toLowerCase();
1172          
1173          if (lowerName.contains("name")) {
1174              return "John Doe";
1175          } else if (lowerName.contains("email")) {
1176              return "john.doe@example.com";
1177          } else if (lowerName.contains("date")) {
1178              return "2024-01-15";
1179          } else if (lowerName.contains("age")) {
1180              return "25";
1181          } else if (lowerName.contains("message") || lowerName.contains("text")) {
1182              return "Sample message for testing";
1183          } else if (lowerName.contains("number") || lowerName.contains("count")) {
1184              return "42";
1185          } else {
1186              return "test_value_" + variableName;
1187          }
1188      }
1189      
1190      // Data classes
1191      @Data
1192      @AllArgsConstructor
1193      @NoArgsConstructor
1194      public static class PromptParameters {
1195          private String promptId;
1196          private String task;
1197          private String workflowId;
1198          private Language language;
1199          private Map<String, Object> parameters;
1200      }
1201      
1202      @Data
1203      @AllArgsConstructor
1204      @NoArgsConstructor
1205      public static class PromptEngineerResult {
1206          private String prompt;
1207          private Object testResult;
1208      }
1209  }
1210  ```
1211  
1212  ## Spring Boot Integration
1213  
1214  ### Service Layer Pattern
1215  
1216  All workflow examples are wrapped in Spring services for dependency injection and configuration:
1217  
1218  ```java
1219  @Service
1220  public class ChatWorkflowService extends ChatWorkflow {
1221      
1222      public ChatWorkflowService(
1223              EtlConfig config,
1224              PromptService promptService,
1225              ModelRequestService modelRequestService,
1226              LLMMemoryProvider memoryProvider,
1227              WorkflowRegistry workflowRegistry) throws Exception {
1228          
1229          super(
1230              ModelClientFactory.fromConfig(config.getVault().get(0)),
1231              promptService,
1232              modelRequestService,
1233              memoryProvider,
1234              workflowRegistry
1235          );
1236      }
1237  }
1238  ```
1239  
1240  ### Configuration Integration
1241  
1242  Workflows automatically integrate with DriftKit configuration:
1243  
1244  ```yaml
1245  driftkit:
1246    vault:
1247      - name: "primary"
1248        type: "openai"
1249        apiKey: "${OPENAI_API_KEY}"
1250        model: "gpt-4"
1251        temperature: 0.7
1252      - name: "claude"
1253        type: "claude"
1254        apiKey: "${CLAUDE_API_KEY}"
1255        model: "claude-sonnet-4-20250514"
1256        temperature: 0.7
1257        
1258    vectorStores:
1259      - name: "primary"
1260        type: "pinecone"
1261        apiKey: "${PINECONE_API_KEY}"
1262        
1263    embeddingServices:
1264      - name: "primary"
1265        type: "openai"
1266        apiKey: "${OPENAI_API_KEY}"
1267        model: "text-embedding-ada-002"
1268  ```
1269  
1270  ## Usage Patterns
1271  
1272  ### Basic Workflow Execution
1273  
1274  ```java
1275  @Service
1276  public class WorkflowOrchestrationService {
1277      
1278      private final WorkflowRegistry workflowRegistry;
1279      
1280      public String processUserMessage(String message, String chatId) throws Exception {
1281          ChatWorkflow.ChatStartEvent startEvent = new ChatWorkflow.ChatStartEvent();
1282          startEvent.setTask(MessageTask.builder()
1283              .message(message)
1284              .chatId(chatId)
1285              .build());
1286          
1287          StopEvent<ChatWorkflow.ChatResult> result = workflowRegistry.executeWorkflow(
1288              "chat-workflow",
1289              startEvent,
1290              new WorkflowContext()
1291          );
1292          
1293          return result.getResult().getResponse();
1294      }
1295  }
1296  ```
1297  
1298  ### Document Ingestion Pipeline
1299  
1300  ```java
1301  @Service
1302  public class DocumentIngestionService {
1303      
1304      private final WorkflowRegistry workflowRegistry;
1305      
1306      public DocumentSaveResult ingestDocument(MultipartFile file, String indexId) throws Exception {
1307          RAGModifyWorkflow.DocumentsEvent startEvent = RAGModifyWorkflow.DocumentsEvent.builder()
1308              .indexId(indexId)
1309              .sourceType("file")
1310              .fileData(file.getBytes())
1311              .mimeType(file.getContentType())
1312              .metadata(Map.of(
1313                  "filename", file.getOriginalFilename(),
1314                  "size", file.getSize()
1315              ))
1316              .build();
1317          
1318          StopEvent<RAGModifyWorkflow.DocumentSaveResult> result = workflowRegistry.executeWorkflow(
1319              "rag-modify-workflow",
1320              startEvent,
1321              new WorkflowContext()
1322          );
1323          
1324          return result.getResult();
1325      }
1326  }
1327  ```
1328  
1329  ### Intelligent Routing
1330  
1331  ```java
1332  @Service
1333  public class MessageRoutingService {
1334      
1335      private final WorkflowRegistry workflowRegistry;
1336      
1337      public RouterResult routeMessage(String message, String chatId) throws Exception {
1338          RouterWorkflow.RouterStartEvent startEvent = new RouterWorkflow.RouterStartEvent();
1339          startEvent.setMessage(message);
1340          startEvent.setChatId(chatId);
1341          
1342          StopEvent<RouterWorkflow.RouterResult> result = workflowRegistry.executeWorkflow(
1343              "router-workflow",
1344              startEvent,
1345              new WorkflowContext()
1346          );
1347          
1348          return result.getResult();
1349      }
1350  }
1351  ```
1352  
1353  ## Testing and Quality Assurance
1354  
1355  ### Workflow Testing Framework
1356  
1357  ```java
1358  @SpringBootTest
1359  class WorkflowIntegrationTest {
1360      
1361      @Autowired
1362      private WorkflowRegistry workflowRegistry;
1363      
1364      @Test
1365      void shouldExecuteChatWorkflow() throws Exception {
1366          ChatWorkflow.ChatStartEvent startEvent = new ChatWorkflow.ChatStartEvent();
1367          startEvent.setTask(MessageTask.builder()
1368              .message("Hello, how can you help me?")
1369              .chatId("test-chat")
1370              .build());
1371          
1372          StopEvent<ChatWorkflow.ChatResult> result = workflowRegistry.executeWorkflow(
1373              "chat-workflow",
1374              startEvent,
1375              new WorkflowContext()
1376          );
1377          
1378          assertThat(result.getResult()).isNotNull();
1379          assertThat(result.getResult().getResponse()).isNotBlank();
1380      }
1381      
1382      @Test
1383      void shouldIngestDocument() throws Exception {
1384          RAGModifyWorkflow.DocumentsEvent startEvent = RAGModifyWorkflow.DocumentsEvent.builder()
1385              .indexId("test-index")
1386              .sourceType("text")
1387              .textContent("This is a test document for ingestion.")
1388              .build();
1389          
1390          StopEvent<RAGModifyWorkflow.DocumentSaveResult> result = workflowRegistry.executeWorkflow(
1391              "rag-modify-workflow",
1392              startEvent,
1393              new WorkflowContext()
1394          );
1395          
1396          assertThat(result.getResult().getTotalDocuments()).isGreaterThan(0);
1397          assertThat(result.getResult().getDocumentIds()).isNotEmpty();
1398      }
1399  }
1400  ```
1401  
1402  ## Performance Considerations
1403  
1404  ### Concurrent Processing
1405  
1406  The RAGModifyWorkflow demonstrates concurrent processing for document chunking and embedding generation:
1407  
1408  ```java
1409  // Process chunks concurrently using ThreadPoolExecutor
1410  List<CompletableFuture<Document>> futures = chunks.stream()
1411      .map(chunk -> CompletableFuture.supplyAsync(() -> {
1412          // Process chunk asynchronously
1413          return processChunk(chunk);
1414      }, executor))
1415      .collect(Collectors.toList());
1416  
1417  // Wait for all chunks to complete
1418  List<Document> documents = futures.stream()
1419      .map(CompletableFuture::join)
1420      .collect(Collectors.toList());
1421  ```
1422  
1423  ### Memory Management
1424  
1425  Workflows implement proper resource cleanup and memory management:
1426  
1427  ```java
1428  @PreDestroy
1429  public void cleanup() {
1430      if (executor != null && !executor.isShutdown()) {
1431          executor.shutdown();
1432          try {
1433              if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
1434                  executor.shutdownNow();
1435              }
1436          } catch (InterruptedException e) {
1437              executor.shutdownNow();
1438              Thread.currentThread().interrupt();
1439          }
1440      }
1441  }
1442  ```
1443  
1444  ## Extension Points
1445  
1446  ### Custom Workflow Development
1447  
1448  Developers should use the `driftkit-workflow-engine-core` fluent API to create custom workflows:
1449  
1450  ```java
1451  @Component
1452  public class CustomWorkflowBuilder {
1453      
1454      public Workflow<CustomInput, CustomOutput> buildWorkflow() {
1455          return WorkflowBuilder.<CustomInput, CustomOutput>create("custom-workflow")
1456              .withDescription("Custom business workflow")
1457              .step("process", ProcessStep.class)
1458              .asyncStep("analyze", AnalysisStep.class, 
1459                  AsyncConfig.builder()
1460                      .timeout(Duration.ofMinutes(5))
1461                      .build())
1462              .branch("decision", DecisionStep.class,
1463                  branchBuilder -> branchBuilder
1464                      .branch("option1", Option1Step.class)
1465                      .branch("option2", Option2Step.class)
1466                      .otherwise(DefaultStep.class))
1467              .step("finalize", FinalStep.class)
1468              .build();
1469      }
1470  }
1471  ```
1472  
1473  ### Integration with External Services
1474  
1475  Workflows can easily integrate with external APIs and services:
1476  
1477  ```java
1478  @Step(name = "callExternalService")
1479  public DataEvent<String> callExternalService(DataEvent<String> input, WorkflowContext context) throws Exception {
1480      ExternalServiceRequest request = new ExternalServiceRequest(input.getData());
1481      ExternalServiceResponse response = externalServiceClient.call(request);
1482      
1483      return DataEvent.of(response.getData(), "processResponse");
1484  }
1485  ```
1486  
1487  This comprehensive documentation demonstrates the power and flexibility of the DriftKit workflows framework through practical, production-ready examples. Each workflow showcases different aspects of AI application development, from conversational interfaces to document processing and intelligent reasoning systems.