circuit_breaker-wf 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/CHANGELOG.md +52 -0
  4. data/Gemfile +10 -0
  5. data/Gemfile.lock +116 -0
  6. data/LICENSE +21 -0
  7. data/README.md +324 -0
  8. data/examples/document/README.md +150 -0
  9. data/examples/document/document_assistant.rb +535 -0
  10. data/examples/document/document_rules.rb +60 -0
  11. data/examples/document/document_token.rb +83 -0
  12. data/examples/document/document_workflow.rb +114 -0
  13. data/examples/document/mock_executor.rb +80 -0
  14. data/lib/circuit_breaker/executors/README.md +664 -0
  15. data/lib/circuit_breaker/executors/agent_executor.rb +187 -0
  16. data/lib/circuit_breaker/executors/assistant_executor.rb +245 -0
  17. data/lib/circuit_breaker/executors/base_executor.rb +56 -0
  18. data/lib/circuit_breaker/executors/docker_executor.rb +56 -0
  19. data/lib/circuit_breaker/executors/dsl.rb +97 -0
  20. data/lib/circuit_breaker/executors/llm/memory.rb +82 -0
  21. data/lib/circuit_breaker/executors/llm/tools.rb +94 -0
  22. data/lib/circuit_breaker/executors/nats_executor.rb +230 -0
  23. data/lib/circuit_breaker/executors/serverless_executor.rb +25 -0
  24. data/lib/circuit_breaker/executors/step_executor.rb +47 -0
  25. data/lib/circuit_breaker/history.rb +81 -0
  26. data/lib/circuit_breaker/rules.rb +251 -0
  27. data/lib/circuit_breaker/templates/mermaid.html.erb +51 -0
  28. data/lib/circuit_breaker/templates/plantuml.html.erb +55 -0
  29. data/lib/circuit_breaker/token.rb +486 -0
  30. data/lib/circuit_breaker/visualizer.rb +173 -0
  31. data/lib/circuit_breaker/workflow_dsl.rb +359 -0
  32. data/lib/circuit_breaker.rb +236 -0
  33. data/workflow-editor/.gitignore +24 -0
  34. data/workflow-editor/README.md +106 -0
  35. data/workflow-editor/eslint.config.js +28 -0
  36. data/workflow-editor/index.html +13 -0
  37. data/workflow-editor/package-lock.json +6864 -0
  38. data/workflow-editor/package.json +50 -0
  39. data/workflow-editor/postcss.config.js +6 -0
  40. data/workflow-editor/public/vite.svg +1 -0
  41. data/workflow-editor/src/App.css +42 -0
  42. data/workflow-editor/src/App.tsx +365 -0
  43. data/workflow-editor/src/assets/react.svg +1 -0
  44. data/workflow-editor/src/components/AddNodeButton.tsx +68 -0
  45. data/workflow-editor/src/components/EdgeDetails.tsx +175 -0
  46. data/workflow-editor/src/components/NodeDetails.tsx +177 -0
  47. data/workflow-editor/src/components/ResizablePanel.tsx +74 -0
  48. data/workflow-editor/src/components/SaveButton.tsx +45 -0
  49. data/workflow-editor/src/config/change_workflow.yaml +59 -0
  50. data/workflow-editor/src/config/constants.ts +11 -0
  51. data/workflow-editor/src/config/flowConfig.ts +189 -0
  52. data/workflow-editor/src/config/uiConfig.ts +77 -0
  53. data/workflow-editor/src/config/workflow.yaml +58 -0
  54. data/workflow-editor/src/hooks/useKeyPress.ts +29 -0
  55. data/workflow-editor/src/index.css +34 -0
  56. data/workflow-editor/src/main.tsx +10 -0
  57. data/workflow-editor/src/server/saveWorkflow.ts +81 -0
  58. data/workflow-editor/src/utils/saveWorkflow.ts +92 -0
  59. data/workflow-editor/src/utils/workflowLoader.ts +26 -0
  60. data/workflow-editor/src/utils/workflowTransformer.ts +91 -0
  61. data/workflow-editor/src/vite-env.d.ts +1 -0
  62. data/workflow-editor/src/yaml.d.ts +4 -0
  63. data/workflow-editor/tailwind.config.js +15 -0
  64. data/workflow-editor/tsconfig.app.json +26 -0
  65. data/workflow-editor/tsconfig.json +7 -0
  66. data/workflow-editor/tsconfig.node.json +24 -0
  67. data/workflow-editor/vite.config.ts +8 -0
  68. metadata +267 -0
@@ -0,0 +1,664 @@
1
+ # Circuit Breaker Executors
2
+
3
+ The Circuit Breaker executor system provides a flexible, DSL-driven approach to defining and running various types of execution environments. Each executor follows a consistent pattern while allowing for specialized configuration and behavior.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Executor DSL](#executor-dsl)
8
+ - [Available Executors](#available-executors)
9
+ - [Creating Custom Executors](#creating-custom-executors)
10
+ - [Examples](#examples)
11
+
12
+ ## Executor DSL
13
+
14
+ The executor DSL provides a declarative way to define:
15
+ - Required and optional parameters
16
+ - Type validations
17
+ - Custom validation rules
18
+ - Execution lifecycle hooks
19
+
20
+ ### Basic Structure
21
+
22
+ ```ruby
23
+ class MyExecutor < BaseExecutor
24
+ executor_config do
25
+ # Parameter definitions
26
+ parameter :name,
27
+ type: :string,
28
+ required: true,
29
+ description: 'Description of the parameter'
30
+
31
+ # Validation rules
32
+ validate do |context|
33
+ # Custom validation logic
34
+ end
35
+
36
+ # Lifecycle hooks
37
+ before_execute do |context|
38
+ # Setup logic
39
+ end
40
+
41
+ after_execute do |result|
42
+ # Cleanup or logging logic
43
+ end
44
+ end
45
+
46
+ protected
47
+
48
+ def execute_internal
49
+ # Implementation
50
+ @result = { status: 'completed' }
51
+ end
52
+ end
53
+ ```
54
+
55
+ ### Parameter Types
56
+
57
+ The DSL supports the following parameter types:
58
+ - `:string`
59
+ - `:integer`
60
+ - `:array`
61
+ - `:hash`
62
+ - `:boolean`
63
+
64
+ Each parameter can be configured with:
65
+ - `required: true/false` - Whether the parameter must be provided
66
+ - `default: value` - Default value if not provided
67
+ - `description: 'text'` - Documentation for the parameter
68
+
69
+ ## Available Executors
70
+
71
+ ### Docker Executor
72
+
73
+ Runs containers with configurable images, commands, and environment.
74
+
75
+ ```ruby
76
+ docker = DockerExecutor.new(
77
+ image: 'nginx:latest',
78
+ command: 'nginx -g "daemon off;"',
79
+ environment: { 'PORT' => '8080' },
80
+ volumes: ['/host/path:/container/path']
81
+ )
82
+ result = docker.execute
83
+ ```
84
+
85
+ ### NATS Executor
86
+
87
+ Manages workflow state and event distribution using NATS.
88
+
89
+ ```ruby
90
+ nats = NatsExecutor.new(
91
+ nats_url: 'nats://localhost:4222',
92
+ petri_net: workflow_definition,
93
+ workflow_id: 'custom-id'
94
+ )
95
+ result = nats.execute
96
+ ```
97
+
98
+ ### Assistant Executor
99
+
100
+ Interacts with AI assistants for natural language processing tasks.
101
+
102
+ ```ruby
103
+ assistant = AssistantExecutor.new(
104
+ model: 'gpt-4',
105
+ system_prompt: 'You are a helpful data analysis assistant',
106
+ input: 'Analyze this data set',
107
+ tools: [DataAnalysisTool.new]
108
+ )
109
+ result = assistant.execute
110
+ ```
111
+
112
+ ### Agent Executor
113
+
114
+ Runs autonomous agents that can perform complex multi-step tasks.
115
+
116
+ ```ruby
117
+ agent = AgentExecutor.new(
118
+ task: 'Analyze the data and create a summary report',
119
+ tools: [DataAnalysisTool.new, SearchTool.new],
120
+ system_prompt: 'You are a data analysis agent',
121
+ max_iterations: 5
122
+ )
123
+ result = agent.execute
124
+ ```
125
+
126
+ ### Serverless Executor
127
+
128
+ Invokes serverless functions with configurable runtimes and payloads.
129
+
130
+ ```ruby
131
+ serverless = ServerlessExecutor.new(
132
+ function_name: 'process-data',
133
+ runtime: 'ruby',
134
+ payload: { data: [1, 2, 3] },
135
+ environment: { 'STAGE' => 'production' }
136
+ )
137
+ result = serverless.execute
138
+ ```
139
+
140
+ ### Step Executor
141
+
142
+ Runs multiple executors in sequence or parallel.
143
+
144
+ ```ruby
145
+ step = StepExecutor.new(
146
+ steps: [
147
+ {
148
+ executor: DockerExecutor,
149
+ context: { image: 'preprocessor:latest' }
150
+ },
151
+ {
152
+ executor: AssistantExecutor,
153
+ context: { prompt: 'Analyze the data' }
154
+ }
155
+ ],
156
+ parallel: true
157
+ )
158
+ result = step.execute
159
+ ```
160
+
161
+ ## Creating Custom Executors
162
+
163
+ To create a custom executor:
164
+
165
+ 1. Create a new class inheriting from `BaseExecutor`
166
+ 2. Define your configuration using the executor DSL
167
+ 3. Implement the `execute_internal` method
168
+
169
+ Example:
170
+
171
+ ```ruby
172
+ class CustomExecutor < BaseExecutor
173
+ executor_config do
174
+ parameter :input_data,
175
+ type: :hash,
176
+ required: true,
177
+ description: 'Data to process'
178
+
179
+ parameter :options,
180
+ type: :hash,
181
+ default: {},
182
+ description: 'Processing options'
183
+
184
+ validate do |context|
185
+ unless context[:input_data].key?(:type)
186
+ raise ArgumentError, 'Input data must specify a type'
187
+ end
188
+ end
189
+
190
+ before_execute do |context|
191
+ puts "Processing #{context[:input_data][:type]} data"
192
+ end
193
+ end
194
+
195
+ protected
196
+
197
+ def execute_internal
198
+ # Process the data
199
+ @result = {
200
+ processed_data: process_data(@context[:input_data]),
201
+ options_used: @context[:options],
202
+ status: 'completed'
203
+ }
204
+ end
205
+
206
+ private
207
+
208
+ def process_data(data)
209
+ # Implementation
210
+ end
211
+ end
212
+ ```
213
+
214
+ ## Using in Workflows
215
+
216
+ Executors can be integrated with Circuit Breaker's rules engine and workflow DSL for sophisticated process control. Here's an example:
217
+
218
+ ```ruby
219
+ # Define rules for executor results
220
+ module ExecutorRules
221
+ def self.define
222
+ CircuitBreaker::RulesEngine::DSL.define do
223
+ # Helper methods for rule conditions
224
+ def meets_quality(threshold)
225
+ ->(token) { token.data[:quality_score] >= threshold }
226
+ end
227
+
228
+ def has_confidence(threshold)
229
+ ->(token) { token.data[:confidence_score] >= threshold }
230
+ end
231
+
232
+ def requires(field)
233
+ ->(token) { !token.data[field].nil? && !token.data[field].empty? }
234
+ end
235
+
236
+ def has_error
237
+ ->(token) { token.data[:status] == 'error' }
238
+ end
239
+
240
+ # Quality Rules
241
+ rule :meets_quality_threshold,
242
+ desc: "Data meets minimum quality requirements",
243
+ &meets_quality(0.8)
244
+
245
+ rule :high_confidence,
246
+ desc: "Analysis has high confidence score",
247
+ &has_confidence(0.9)
248
+
249
+ # Data Rules
250
+ rule :has_processed_data,
251
+ desc: "Processing step produced output data",
252
+ &requires(:processed_output)
253
+
254
+ rule :has_analysis_results,
255
+ desc: "Analysis step produced results",
256
+ &requires(:analysis_results)
257
+
258
+ # Error Rules
259
+ rule :processing_error,
260
+ desc: "Processing step encountered an error",
261
+ &has_error
262
+ end
263
+ end
264
+ end
265
+
266
+ # Define the workflow
267
+ workflow = CircuitBreaker::WorkflowDSL.define(rules: ExecutorRules.define) do
268
+ # Define all possible states
269
+ states :raw_data, # Initial state with raw data
270
+ :processed, # Data has been processed
271
+ :analyzed, # Data has been analyzed
272
+ :human_review, # Needs human review
273
+ :completed # Process completed
274
+
275
+ # Process raw data
276
+ flow(:raw_data >> :processed)
277
+ .transition(:process)
278
+ .policy(
279
+ validations: { all: [:input_file] },
280
+ rules: { all: [:has_processed_data] }
281
+ ) do
282
+ docker = DockerExecutor.new(
283
+ image: 'data-processor:latest',
284
+ command: './process.sh',
285
+ environment: { 'INPUT': token.data[:input_file] }
286
+ )
287
+ result = docker.execute
288
+ token.data.merge!(result)
289
+ end
290
+
291
+ # Analyze processed data
292
+ flow(:processed >> :analyzed)
293
+ .transition(:analyze)
294
+ .policy(
295
+ validations: { all: [:processed_output] },
296
+ rules: {
297
+ all: [:meets_quality_threshold],
298
+ any: [:high_confidence]
299
+ }
300
+ ) do
301
+ assistant = AssistantExecutor.new(
302
+ model: 'gpt-4',
303
+ input: "Analyze the processed data: #{token.data[:processed_output]}",
304
+ tools: [DataAnalysisTool.new]
305
+ )
306
+ result = assistant.execute
307
+ token.data.merge!(result)
308
+ end
309
+
310
+ # Route to human review if needed
311
+ flow(:analyzed >> :human_review)
312
+ .transition(:request_review)
313
+ .policy(
314
+ rules: {
315
+ any: [:processing_error]
316
+ }
317
+ ) do
318
+ agent = AgentExecutor.new(
319
+ task: 'Prepare data for human review',
320
+ tools: [ReportGeneratorTool.new],
321
+ context: { data: token.data }
322
+ )
323
+ result = agent.execute
324
+ token.data[:review_package] = result[:report]
325
+ end
326
+
327
+ # Complete the process
328
+ flow(:analyzed >> :completed)
329
+ .transition(:complete)
330
+ .policy(
331
+ rules: {
332
+ all: [:has_analysis_results, :high_confidence]
333
+ }
334
+ )
335
+ end
336
+
337
+ # Start the workflow with initial data
338
+ token = CircuitBreaker::Token.new(
339
+ data: {
340
+ input_file: 'data.json',
341
+ quality_threshold: 0.8,
342
+ confidence_threshold: 0.9
343
+ }
344
+ )
345
+ workflow.add_token(token)
346
+ ```
347
+
348
+ This example demonstrates:
349
+
350
+ 1. **Rule Definition**
351
+ - Rules are defined using the DSL pattern
352
+ - Helper methods create reusable conditions
353
+ - Rules focus on executor results and data quality
354
+
355
+ 2. **Workflow Structure**
356
+ - Clear state definitions
357
+ - Transitions with policies
358
+ - Validation and rule requirements
359
+
360
+ 3. **Executor Integration**
361
+ - Results stored in token data
362
+ - Rules evaluate executor outputs
363
+ - Data flows between executors
364
+
365
+ 4. **Policy Controls**
366
+ - Quality thresholds
367
+ - Confidence requirements
368
+ - Error handling paths
369
+
370
+ The workflow ensures that:
371
+ - Data meets quality requirements before analysis
372
+ - Low confidence results get human review
373
+ - Errors are properly handled
374
+ - Process completion requires all quality checks
375
+
376
+ ## Best Practices
377
+
378
+ 1. Always define parameter types and requirements clearly
379
+ 2. Use validation rules to catch configuration errors early
380
+ 3. Implement before/after hooks for setup and cleanup
381
+ 4. Return structured results that can be used by other executors
382
+ 5. Handle errors gracefully and provide meaningful error messages
383
+ 6. Document any special requirements or dependencies
384
+
385
+ ## Assistant Executor
386
+
387
+ A DSL-driven executor for building AI assistants with tool integration and context management.
388
+
389
+ ```ruby
390
+ assistant = CircuitBreaker::Executors::AssistantExecutor.define do
391
+ use_model 'qwen2.5-coder'
392
+ with_system_prompt "You are a specialized assistant..."
393
+ with_parameters temperature: 0.7, top_p: 0.9
394
+ add_tools [AnalysisTool.new, SentimentTool.new]
395
+ end
396
+
397
+ result = assistant
398
+ .update_context(input: "Analyze this document...")
399
+ .execute
400
+ ```
401
+
402
+ Features:
403
+ - Fluent DSL interface
404
+ - Automatic model provider detection
405
+ - Tool integration and management
406
+ - Context persistence
407
+ - Error handling with retries
408
+ - Parameter validation
409
+
410
+ Configuration Options:
411
+ ```ruby
412
+ executor_config do
413
+ parameter :model, type: :string, default: 'gpt-4'
414
+ parameter :model_provider, type: :string
415
+ parameter :ollama_base_url, type: :string, default: 'http://localhost:11434'
416
+ parameter :system_prompt, type: :string
417
+ parameter :tools, type: :array, default: []
418
+ parameter :parameters, type: :hash, default: {}
419
+ parameter :input, type: :string
420
+
421
+ validate do |context|
422
+ # Automatic model provider detection
423
+ if context[:model_provider].nil?
424
+ context[:model_provider] = if context[:model].to_s.start_with?('llama', 'codellama', 'mistral')
425
+ 'ollama'
426
+ else
427
+ 'openai'
428
+ end
429
+ end
430
+ end
431
+
432
+ before_execute do |context|
433
+ # Initialize memory and tools
434
+ @memory.system_prompt = context[:system_prompt] if context[:system_prompt]
435
+ add_tools(context[:tools]) if context[:tools]
436
+ end
437
+ end
438
+ ```
439
+
440
+ ### Tool Integration
441
+
442
+ #### Creating Custom Tools
443
+
444
+ 1. Basic Tool:
445
+ ```ruby
446
+ class CustomTool < CircuitBreaker::Executors::LLM::Tool
447
+ def initialize
448
+ super(
449
+ name: 'custom_tool',
450
+ description: 'Performs custom analysis',
451
+ parameters: {
452
+ input: { type: 'string', description: 'Input to analyze' }
453
+ }
454
+ )
455
+ end
456
+
457
+ def execute(input:)
458
+ # Tool implementation
459
+ { result: analyze(input) }
460
+ end
461
+ end
462
+ ```
463
+
464
+ 2. Chainable Tool:
465
+ ```ruby
466
+ class ChainableTool < CircuitBreaker::Executors::LLM::ChainableTool
467
+ def initialize
468
+ super(
469
+ name: 'chainable_tool',
470
+ description: 'Can be chained with other tools',
471
+ input_schema: { type: 'string' },
472
+ output_schema: { type: 'object' }
473
+ )
474
+ end
475
+
476
+ def execute(input, context)
477
+ # Implementation with access to execution context
478
+ next_tool = context.available_tools.find { |t| t.can_handle?(result) }
479
+ context.chain(next_tool) if next_tool
480
+ end
481
+ end
482
+ ```
483
+
484
+ #### Tool Management
485
+
486
+ ```ruby
487
+ # Add individual tools
488
+ assistant.add_tool(CustomTool.new)
489
+
490
+ # Add multiple tools
491
+ assistant.add_tools([
492
+ AnalysisTool.new,
493
+ SentimentTool.new,
494
+ CustomTool.new
495
+ ])
496
+
497
+ # Remove tool
498
+ assistant.remove_tool('custom_tool')
499
+
500
+ # Clear all tools
501
+ assistant.clear_tools
502
+ ```
503
+
504
+ ## Agent Executor
505
+
506
+ A powerful executor for autonomous agents that can plan and execute multi-step tasks.
507
+
508
+ ```ruby
509
+ agent = CircuitBreaker::Executors::AgentExecutor.define do
510
+ use_model 'gpt-4'
511
+ with_system_prompt "You are a task planning agent..."
512
+ with_tools AgentTools.default_toolset
513
+ with_memory_size 10
514
+ end
515
+
516
+ result = agent
517
+ .update_context(task: "Research and summarize...")
518
+ .execute
519
+ ```
520
+
521
+ Features:
522
+ - Task planning and execution
523
+ - Tool discovery and selection
524
+ - Memory management
525
+ - Error recovery
526
+ - Progress tracking
527
+
528
+ ## Error Handling
529
+
530
+ The executors include comprehensive error handling:
531
+
532
+ 1. Connection Errors:
533
+ ```ruby
534
+ def make_ollama_request(context, retries = 3)
535
+ # ... request setup ...
536
+ begin
537
+ response = http.request(request)
538
+ # ... process response ...
539
+ rescue => e
540
+ if retries > 0
541
+ sleep(2)
542
+ make_ollama_request(context, retries - 1)
543
+ else
544
+ handle_error(e)
545
+ end
546
+ end
547
+ end
548
+ ```
549
+
550
+ 2. Validation Errors:
551
+ ```ruby
552
+ validate do |context|
553
+ raise "Missing required parameter: model" if context[:model].nil?
554
+ raise "Invalid temperature value" unless valid_temperature?(context[:parameters][:temperature])
555
+ end
556
+ ```
557
+
558
+ 3. Tool Execution Errors:
559
+ ```ruby
560
+ def execute_tool(tool, params)
561
+ tool.execute(**params)
562
+ rescue => e
563
+ {
564
+ error: "Tool execution failed: #{e.message}",
565
+ fallback: tool.fallback_response
566
+ }
567
+ end
568
+ ```
569
+
570
+ ## Memory Management
571
+
572
+ Both executors support conversation memory management:
573
+
574
+ ```ruby
575
+ # Initialize memory
576
+ @memory = LLM::ConversationMemory.new(
577
+ system_prompt: "Initial prompt...",
578
+ max_tokens: 4000
579
+ )
580
+
581
+ # Update memory
582
+ @memory.add_message(role: :user, content: "New message")
583
+ @memory.add_message(role: :assistant, content: "Response")
584
+
585
+ # Clear memory
586
+ @memory.clear
587
+
588
+ # Get conversation history
589
+ history = @memory.messages
590
+ ```
591
+
592
+ ## Best Practices
593
+
594
+ 1. Model Selection:
595
+ - Use appropriate models for tasks
596
+ - Consider token limits
597
+ - Balance performance and cost
598
+
599
+ 2. Tool Design:
600
+ - Keep tools focused and simple
601
+ - Provide clear descriptions
602
+ - Include fallback responses
603
+ - Handle errors gracefully
604
+
605
+ 3. Memory Management:
606
+ - Set appropriate memory limits
607
+ - Clear memory when needed
608
+ - Monitor token usage
609
+
610
+ 4. Error Handling:
611
+ - Implement retries for transient errors
612
+ - Provide helpful error messages
613
+ - Include fallback behaviors
614
+
615
+ ## Configuration Examples
616
+
617
+ ### 1. Document Analysis Assistant
618
+
619
+ ```ruby
620
+ assistant = AssistantExecutor.define do
621
+ use_model 'qwen2.5-coder'
622
+ with_system_prompt <<~PROMPT
623
+ You are a document analysis assistant...
624
+ PROMPT
625
+ with_parameters(
626
+ temperature: 0.7,
627
+ top_p: 0.9,
628
+ top_k: 40
629
+ )
630
+ add_tools [
631
+ ContentAnalysisTool.new,
632
+ SentimentAnalysisTool.new,
633
+ ImprovementTool.new
634
+ ]
635
+ end
636
+ ```
637
+
638
+ ### 2. Research Agent
639
+
640
+ ```ruby
641
+ agent = AgentExecutor.define do
642
+ use_model 'gpt-4'
643
+ with_system_prompt <<~PROMPT
644
+ You are a research agent...
645
+ PROMPT
646
+ with_tools [
647
+ SearchTool.new,
648
+ SummarizeTool.new,
649
+ CitationTool.new
650
+ ]
651
+ with_memory_size 5
652
+ end
653
+ ```
654
+
655
+ ## Contributing
656
+
657
+ 1. Follow the DSL patterns
658
+ 2. Add comprehensive tests
659
+ 3. Document new features
660
+ 4. Handle errors appropriately
661
+
662
+ ## License
663
+
664
+ This module is part of the Circuit Breaker library and is available under the MIT license.