conductor_ruby 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 (143) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +142 -0
  3. data/LICENSE +190 -0
  4. data/README.md +517 -0
  5. data/examples/agentic_workflows/llm_chat.rb +106 -0
  6. data/examples/dynamic_workflow.rb +177 -0
  7. data/examples/event_handler.rb +94 -0
  8. data/examples/event_listener_examples.rb +430 -0
  9. data/examples/helloworld/greetings_worker.rb +24 -0
  10. data/examples/helloworld/helloworld.rb +99 -0
  11. data/examples/kitchensink.rb +213 -0
  12. data/examples/metadata_journey.rb +189 -0
  13. data/examples/metrics_example.rb +284 -0
  14. data/examples/new_dsl_demo.rb +141 -0
  15. data/examples/orkes/http_poll.rb +83 -0
  16. data/examples/orkes/secrets_example.rb +69 -0
  17. data/examples/orkes/wait_for_webhook.rb +90 -0
  18. data/examples/prompt_journey.rb +245 -0
  19. data/examples/rag_workflow.rb +167 -0
  20. data/examples/schedule_journey.rb +244 -0
  21. data/examples/simple_worker.rb +125 -0
  22. data/examples/simple_workflow.rb +89 -0
  23. data/examples/task_context_example.rb +257 -0
  24. data/examples/task_listener_example.rb +192 -0
  25. data/examples/worker_configuration_example.rb +282 -0
  26. data/examples/workflow_dsl.rb +316 -0
  27. data/examples/workflow_ops.rb +305 -0
  28. data/lib/conductor/client/authorization_client.rb +238 -0
  29. data/lib/conductor/client/integration_client.rb +108 -0
  30. data/lib/conductor/client/metadata_client.rb +139 -0
  31. data/lib/conductor/client/prompt_client.rb +58 -0
  32. data/lib/conductor/client/scheduler_client.rb +132 -0
  33. data/lib/conductor/client/schema_client.rb +32 -0
  34. data/lib/conductor/client/secret_client.rb +48 -0
  35. data/lib/conductor/client/task_client.rb +168 -0
  36. data/lib/conductor/client/workflow_client.rb +242 -0
  37. data/lib/conductor/configuration/authentication_settings.rb +17 -0
  38. data/lib/conductor/configuration.rb +103 -0
  39. data/lib/conductor/exceptions.rb +86 -0
  40. data/lib/conductor/http/api/application_resource_api.rb +107 -0
  41. data/lib/conductor/http/api/authorization_resource_api.rb +56 -0
  42. data/lib/conductor/http/api/event_resource_api.rb +133 -0
  43. data/lib/conductor/http/api/gateway_auth_resource_api.rb +48 -0
  44. data/lib/conductor/http/api/group_resource_api.rb +76 -0
  45. data/lib/conductor/http/api/integration_resource_api.rb +145 -0
  46. data/lib/conductor/http/api/metadata_resource_api.rb +231 -0
  47. data/lib/conductor/http/api/prompt_resource_api.rb +81 -0
  48. data/lib/conductor/http/api/role_resource_api.rb +60 -0
  49. data/lib/conductor/http/api/scheduler_resource_api.rb +211 -0
  50. data/lib/conductor/http/api/schema_resource_api.rb +82 -0
  51. data/lib/conductor/http/api/secret_resource_api.rb +134 -0
  52. data/lib/conductor/http/api/task_resource_api.rb +321 -0
  53. data/lib/conductor/http/api/token_resource_api.rb +42 -0
  54. data/lib/conductor/http/api/user_resource_api.rb +59 -0
  55. data/lib/conductor/http/api/workflow_bulk_resource_api.rb +91 -0
  56. data/lib/conductor/http/api/workflow_resource_api.rb +451 -0
  57. data/lib/conductor/http/api_client.rb +437 -0
  58. data/lib/conductor/http/models/authentication_config.rb +67 -0
  59. data/lib/conductor/http/models/authorization_request.rb +39 -0
  60. data/lib/conductor/http/models/base_model.rb +162 -0
  61. data/lib/conductor/http/models/bulk_response.rb +39 -0
  62. data/lib/conductor/http/models/conductor_application.rb +39 -0
  63. data/lib/conductor/http/models/conductor_user.rb +53 -0
  64. data/lib/conductor/http/models/create_or_update_application_request.rb +24 -0
  65. data/lib/conductor/http/models/create_or_update_role_request.rb +27 -0
  66. data/lib/conductor/http/models/event_handler.rb +130 -0
  67. data/lib/conductor/http/models/generate_token_request.rb +27 -0
  68. data/lib/conductor/http/models/group.rb +36 -0
  69. data/lib/conductor/http/models/integration.rb +70 -0
  70. data/lib/conductor/http/models/integration_api.rb +53 -0
  71. data/lib/conductor/http/models/integration_api_update.rb +43 -0
  72. data/lib/conductor/http/models/integration_update.rb +36 -0
  73. data/lib/conductor/http/models/permission.rb +24 -0
  74. data/lib/conductor/http/models/poll_data.rb +33 -0
  75. data/lib/conductor/http/models/prompt_template.rb +59 -0
  76. data/lib/conductor/http/models/prompt_template_test_request.rb +43 -0
  77. data/lib/conductor/http/models/rerun_workflow_request.rb +37 -0
  78. data/lib/conductor/http/models/role.rb +27 -0
  79. data/lib/conductor/http/models/schema_def.rb +59 -0
  80. data/lib/conductor/http/models/search_result.rb +187 -0
  81. data/lib/conductor/http/models/skip_task_request.rb +27 -0
  82. data/lib/conductor/http/models/start_workflow_request.rb +68 -0
  83. data/lib/conductor/http/models/subject_ref.rb +35 -0
  84. data/lib/conductor/http/models/tag_object.rb +36 -0
  85. data/lib/conductor/http/models/target_ref.rb +39 -0
  86. data/lib/conductor/http/models/task.rb +156 -0
  87. data/lib/conductor/http/models/task_def.rb +95 -0
  88. data/lib/conductor/http/models/task_exec_log.rb +30 -0
  89. data/lib/conductor/http/models/task_result.rb +115 -0
  90. data/lib/conductor/http/models/task_result_status.rb +24 -0
  91. data/lib/conductor/http/models/token.rb +33 -0
  92. data/lib/conductor/http/models/upsert_group_request.rb +30 -0
  93. data/lib/conductor/http/models/upsert_user_request.rb +39 -0
  94. data/lib/conductor/http/models/workflow.rb +202 -0
  95. data/lib/conductor/http/models/workflow_def.rb +73 -0
  96. data/lib/conductor/http/models/workflow_schedule.rb +100 -0
  97. data/lib/conductor/http/models/workflow_state_update.rb +30 -0
  98. data/lib/conductor/http/models/workflow_status_constants.rb +57 -0
  99. data/lib/conductor/http/models/workflow_task.rb +169 -0
  100. data/lib/conductor/http/models/workflow_test_request.rb +67 -0
  101. data/lib/conductor/http/rest_client.rb +211 -0
  102. data/lib/conductor/orkes/models/access_key.rb +56 -0
  103. data/lib/conductor/orkes/models/granted_permission.rb +27 -0
  104. data/lib/conductor/orkes/models/metadata_tag.rb +15 -0
  105. data/lib/conductor/orkes/models/rate_limit_tag.rb +15 -0
  106. data/lib/conductor/orkes/orkes_clients.rb +69 -0
  107. data/lib/conductor/version.rb +5 -0
  108. data/lib/conductor/worker/events/conductor_event.rb +40 -0
  109. data/lib/conductor/worker/events/global_dispatcher.rb +37 -0
  110. data/lib/conductor/worker/events/http_events.rb +25 -0
  111. data/lib/conductor/worker/events/listener_registry.rb +40 -0
  112. data/lib/conductor/worker/events/listeners.rb +34 -0
  113. data/lib/conductor/worker/events/sync_event_dispatcher.rb +78 -0
  114. data/lib/conductor/worker/events/task_runner_events.rb +271 -0
  115. data/lib/conductor/worker/events/workflow_events.rb +49 -0
  116. data/lib/conductor/worker/fiber_executor.rb +532 -0
  117. data/lib/conductor/worker/ractor_task_runner.rb +501 -0
  118. data/lib/conductor/worker/task_context.rb +114 -0
  119. data/lib/conductor/worker/task_definition_registrar.rb +322 -0
  120. data/lib/conductor/worker/task_handler.rb +360 -0
  121. data/lib/conductor/worker/task_in_progress.rb +60 -0
  122. data/lib/conductor/worker/task_runner.rb +538 -0
  123. data/lib/conductor/worker/telemetry/metrics_collector.rb +196 -0
  124. data/lib/conductor/worker/telemetry/prometheus_backend.rb +224 -0
  125. data/lib/conductor/worker/worker.rb +355 -0
  126. data/lib/conductor/worker/worker_config.rb +154 -0
  127. data/lib/conductor/worker/worker_registry.rb +71 -0
  128. data/lib/conductor/workflow/dsl/input_ref.rb +37 -0
  129. data/lib/conductor/workflow/dsl/output_ref.rb +44 -0
  130. data/lib/conductor/workflow/dsl/parallel_builder.rb +49 -0
  131. data/lib/conductor/workflow/dsl/switch_builder.rb +74 -0
  132. data/lib/conductor/workflow/dsl/task_ref.rb +178 -0
  133. data/lib/conductor/workflow/dsl/workflow_builder.rb +1016 -0
  134. data/lib/conductor/workflow/dsl/workflow_definition.rb +150 -0
  135. data/lib/conductor/workflow/llm/chat_message.rb +47 -0
  136. data/lib/conductor/workflow/llm/embedding_model.rb +19 -0
  137. data/lib/conductor/workflow/llm/tool_call.rb +43 -0
  138. data/lib/conductor/workflow/llm/tool_spec.rb +46 -0
  139. data/lib/conductor/workflow/task_type.rb +68 -0
  140. data/lib/conductor/workflow/timeout_policy.rb +31 -0
  141. data/lib/conductor/workflow/workflow_executor.rb +373 -0
  142. data/lib/conductor.rb +192 -0
  143. metadata +359 -0
@@ -0,0 +1,245 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Prompt Management Journey - Comprehensive Example
5
+ #
6
+ # Demonstrates all Prompt Management APIs through building an
7
+ # AI-powered customer service system.
8
+ #
9
+ # APIs Covered:
10
+ # - save_prompt, get_prompt, get_prompts, delete_prompt
11
+ # - get_tags_for_prompt_template, update_tag_for_prompt_template, delete_tag_for_prompt_template
12
+ # - test_prompt
13
+ #
14
+ # Usage:
15
+ # bundle exec ruby examples/prompt_journey.rb
16
+
17
+ require_relative '../lib/conductor'
18
+
19
+ class PromptJourney
20
+ AI_INTEGRATION = ENV.fetch('AI_INTEGRATION', 'openai')
21
+ AI_MODEL = ENV.fetch('AI_MODEL', 'gpt-4o-mini')
22
+
23
+ def initialize
24
+ @config = Conductor::Configuration.new
25
+ @clients = Conductor::Orkes::OrkesClients.new(@config)
26
+ @prompt_client = @clients.get_prompt_client
27
+ @created_prompts = []
28
+
29
+ puts '=' * 70
30
+ puts 'Prompt Management Journey'
31
+ puts '=' * 70
32
+ puts "Server: #{@config.server_url}"
33
+ puts "AI Integration: #{AI_INTEGRATION}"
34
+ puts
35
+ end
36
+
37
+ def run
38
+ create_prompts
39
+ retrieve_prompts
40
+ test_prompts
41
+ manage_tags
42
+ cleanup
43
+ end
44
+
45
+ private
46
+
47
+ def create_prompts
48
+ puts "\n--- Creating Prompt Templates ---"
49
+
50
+ # Customer service greeting prompt
51
+ greeting_prompt = <<~PROMPT
52
+ You are a friendly customer service representative for TechMart.
53
+
54
+ Customer Name: ${customer_name}
55
+ Issue Category: ${issue_category}
56
+
57
+ Please greet the customer warmly and ask how you can help them today.
58
+ Keep your response concise and professional.
59
+ PROMPT
60
+
61
+ @prompt_client.save_prompt(
62
+ 'cs_greeting_ruby',
63
+ greeting_prompt,
64
+ description: 'Customer service greeting template'
65
+ )
66
+ @created_prompts << 'cs_greeting_ruby'
67
+ puts 'Created prompt: cs_greeting_ruby'
68
+
69
+ # Product recommendation prompt
70
+ recommendation_prompt = <<~PROMPT
71
+ Based on the following customer preferences, recommend 3 products:
72
+
73
+ Budget: ${budget}
74
+ Category: ${category}
75
+ Previous Purchases: ${previous_purchases}
76
+
77
+ Format your response as a numbered list with brief descriptions.
78
+ PROMPT
79
+
80
+ @prompt_client.save_prompt(
81
+ 'product_recommendation_ruby',
82
+ recommendation_prompt,
83
+ description: 'Product recommendation template'
84
+ )
85
+ @created_prompts << 'product_recommendation_ruby'
86
+ puts 'Created prompt: product_recommendation_ruby'
87
+
88
+ # Issue resolution prompt
89
+ resolution_prompt = <<~PROMPT
90
+ You are helping resolve a customer issue.
91
+
92
+ Issue: ${issue_description}
93
+ Product: ${product_name}
94
+ Purchase Date: ${purchase_date}
95
+
96
+ Provide a helpful resolution or next steps. Be empathetic and solution-focused.
97
+ PROMPT
98
+
99
+ @prompt_client.save_prompt(
100
+ 'issue_resolution_ruby',
101
+ resolution_prompt,
102
+ description: 'Issue resolution template'
103
+ )
104
+ @created_prompts << 'issue_resolution_ruby'
105
+ puts 'Created prompt: issue_resolution_ruby'
106
+ end
107
+
108
+ def retrieve_prompts
109
+ puts "\n--- Retrieving Prompts ---"
110
+
111
+ # Get specific prompt
112
+ prompt = @prompt_client.get_prompt('cs_greeting_ruby')
113
+ puts 'Retrieved prompt: cs_greeting_ruby'
114
+ puts " Description: #{begin
115
+ prompt['description'] || prompt.description
116
+ rescue StandardError
117
+ 'N/A'
118
+ end}"
119
+
120
+ # Get all prompts
121
+ all_prompts = @prompt_client.get_prompts
122
+ puts "\nAll prompts (#{all_prompts.length} total):"
123
+ all_prompts.first(5).each do |p|
124
+ name = p.is_a?(Hash) ? p['name'] : p.name
125
+ puts " - #{name}"
126
+ end
127
+ end
128
+
129
+ def test_prompts
130
+ puts "\n--- Testing Prompts ---"
131
+
132
+ # Test the greeting prompt
133
+ test_input = {
134
+ 'customer_name' => 'John Smith',
135
+ 'issue_category' => 'Technical Support'
136
+ }
137
+
138
+ puts 'Testing cs_greeting_ruby with:'
139
+ test_input.each { |k, v| puts " #{k}: #{v}" }
140
+
141
+ begin
142
+ result = @prompt_client.test_prompt(
143
+ 'cs_greeting_ruby',
144
+ test_input,
145
+ AI_INTEGRATION,
146
+ AI_MODEL
147
+ )
148
+
149
+ puts "\nAI Response:"
150
+ puts '-' * 40
151
+ response = begin
152
+ result.is_a?(Hash) ? result['response'] : result.response
153
+ rescue StandardError
154
+ result
155
+ end
156
+ puts response.to_s[0..500]
157
+ puts '-' * 40
158
+ rescue Conductor::ApiError => e
159
+ puts "Test failed (AI integration may not be configured): #{e.message}"
160
+ end
161
+
162
+ # Test product recommendation
163
+ puts "\nTesting product_recommendation_ruby..."
164
+ rec_input = {
165
+ 'budget' => '$500',
166
+ 'category' => 'Electronics',
167
+ 'previous_purchases' => 'Laptop, Headphones'
168
+ }
169
+
170
+ begin
171
+ result = @prompt_client.test_prompt(
172
+ 'product_recommendation_ruby',
173
+ rec_input,
174
+ AI_INTEGRATION,
175
+ AI_MODEL
176
+ )
177
+ response = begin
178
+ result.is_a?(Hash) ? result['response'] : result.response
179
+ rescue StandardError
180
+ result
181
+ end
182
+ puts "Response preview: #{response.to_s[0..200]}..."
183
+ rescue Conductor::ApiError => e
184
+ puts "Test failed: #{e.message}"
185
+ end
186
+ end
187
+
188
+ def manage_tags
189
+ puts "\n--- Managing Tags ---"
190
+
191
+ tags = [
192
+ { 'key' => 'department', 'value' => 'customer_service' },
193
+ { 'key' => 'language', 'value' => 'english' },
194
+ { 'key' => 'version', 'value' => 'v1' }
195
+ ]
196
+
197
+ # Add tags
198
+ puts 'Adding tags to cs_greeting_ruby...'
199
+ @prompt_client.update_tag_for_prompt_template('cs_greeting_ruby', tags)
200
+
201
+ # Get tags
202
+ retrieved_tags = @prompt_client.get_tags_for_prompt_template('cs_greeting_ruby')
203
+ puts 'Retrieved tags:'
204
+ retrieved_tags.each do |tag|
205
+ key = tag.is_a?(Hash) ? tag['key'] : tag.key
206
+ value = tag.is_a?(Hash) ? tag['value'] : tag.value
207
+ puts " #{key}: #{value}"
208
+ end
209
+
210
+ # Delete specific tag
211
+ puts "\nDeleting 'version' tag..."
212
+ @prompt_client.delete_tag_for_prompt_template(
213
+ 'cs_greeting_ruby',
214
+ [{ 'key' => 'version', 'value' => 'v1' }]
215
+ )
216
+
217
+ # Verify deletion
218
+ remaining_tags = @prompt_client.get_tags_for_prompt_template('cs_greeting_ruby')
219
+ puts "Remaining tags: #{remaining_tags.length}"
220
+ end
221
+
222
+ def cleanup
223
+ puts "\n--- Cleanup ---"
224
+
225
+ @created_prompts.each do |name|
226
+ @prompt_client.delete_prompt(name)
227
+ puts "Deleted prompt: #{name}"
228
+ rescue StandardError => e
229
+ puts "Could not delete #{name}: #{e.message}"
230
+ end
231
+
232
+ puts "\nPrompt journey complete!"
233
+ end
234
+ end
235
+
236
+ if __FILE__ == $PROGRAM_NAME
237
+ begin
238
+ PromptJourney.new.run
239
+ rescue Conductor::ApiError => e
240
+ puts "API Error: #{e.message}"
241
+ rescue StandardError => e
242
+ puts "Error: #{e.message}"
243
+ puts e.backtrace.first(3).join("\n")
244
+ end
245
+ end
@@ -0,0 +1,167 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # RAG (Retrieval Augmented Generation) Workflow Example
5
+ #
6
+ # This example demonstrates a complete RAG pipeline using Conductor:
7
+ # 1. User provides text content as workflow input
8
+ # 2. Conductor indexes the text into a vector DB using embeddings
9
+ # 3. A search query retrieves relevant context from the vector store
10
+ # 4. An LLM generates an answer grounded in the retrieved context
11
+ #
12
+ # Prerequisites:
13
+ # 1. Orkes Conductor server with AI/LLM support
14
+ # 2. Configure integrations in Conductor:
15
+ # - Vector DB integration (e.g., "pinecone", "weaviate", "pgvector")
16
+ # - LLM provider (e.g., "openai", "azure_openai", "cohere")
17
+ #
18
+ # Usage:
19
+ # bundle exec ruby examples/rag_workflow.rb
20
+
21
+ require_relative '../lib/conductor'
22
+
23
+ include Conductor::Workflow
24
+
25
+ # Configuration constants - adjust based on your integrations
26
+ VECTOR_DB = 'pinecone'
27
+ VECTOR_INDEX = 'demo_index'
28
+ NAMESPACE = 'demo_namespace'
29
+ EMBEDDING_PROVIDER = 'openai'
30
+ EMBEDDING_MODEL = 'text-embedding-3-small'
31
+ LLM_PROVIDER = 'openai'
32
+ LLM_MODEL = 'gpt-4o-mini'
33
+
34
+ def create_rag_workflow(workflow_client, workflow_executor)
35
+ workflow = ConductorWorkflow.new(
36
+ workflow_client,
37
+ 'rag_pipeline_ruby',
38
+ version: 1,
39
+ executor: workflow_executor
40
+ )
41
+ workflow.description('RAG pipeline: index text -> search -> generate answer')
42
+ workflow.timeout_seconds(300)
43
+
44
+ # Step 1: Index the input text into vector DB
45
+ index_task = LlmIndexTextTask.new(
46
+ 'index_text_ref',
47
+ VECTOR_DB,
48
+ VECTOR_INDEX,
49
+ namespace: NAMESPACE,
50
+ embedding_model: EMBEDDING_MODEL,
51
+ embedding_model_provider: EMBEDDING_PROVIDER
52
+ )
53
+ index_task.input('text', workflow.input('text'))
54
+ index_task.input('docId', workflow.input('doc_id'))
55
+
56
+ # Step 2: Wait for vector DB to commit (eventual consistency)
57
+ wait_task = WaitTask.new('wait_for_index', wait_for_seconds: 3)
58
+
59
+ # Step 3: Search the index with the user's question
60
+ search_task = LlmSearchIndexTask.new(
61
+ 'search_index_ref',
62
+ VECTOR_DB,
63
+ VECTOR_INDEX,
64
+ namespace: NAMESPACE,
65
+ embedding_model: EMBEDDING_MODEL,
66
+ embedding_model_provider: EMBEDDING_PROVIDER
67
+ )
68
+ search_task.input('query', workflow.input('question'))
69
+ search_task.input('maxResults', 5)
70
+
71
+ # Step 4: Generate answer using retrieved context
72
+ system_prompt = <<~PROMPT
73
+ You are a helpful assistant. Answer the user's question based ONLY on the#{' '}
74
+ context provided below. If the context does not contain enough information, say so.
75
+
76
+ Context from knowledge base:
77
+ ${search_index_ref.output.result}
78
+ PROMPT
79
+
80
+ answer_task = LlmChatCompleteTask.new(
81
+ 'generate_answer_ref',
82
+ LLM_PROVIDER,
83
+ LLM_MODEL,
84
+ messages: [
85
+ ChatMessage.new(role: 'system', message: system_prompt),
86
+ ChatMessage.new(role: 'user', message: workflow.input('question'))
87
+ ],
88
+ temperature: 0.2,
89
+ max_tokens: 1024
90
+ )
91
+
92
+ # Chain tasks
93
+ workflow >> index_task >> wait_task >> search_task >> answer_task
94
+
95
+ # Define outputs
96
+ workflow.output_parameter('retrieved_context', search_task.output('result'))
97
+ workflow.output_parameter('answer', answer_task.output('result'))
98
+
99
+ workflow
100
+ end
101
+
102
+ def main
103
+ config = Conductor::Configuration.new
104
+
105
+ puts '=' * 70
106
+ puts 'Conductor Ruby SDK - RAG Workflow Example'
107
+ puts '=' * 70
108
+ puts
109
+ puts "Server: #{config.server_url}"
110
+ puts "Vector DB: #{VECTOR_DB}"
111
+ puts "LLM Provider: #{LLM_PROVIDER}"
112
+ puts
113
+
114
+ clients = Conductor::Orkes::OrkesClients.new(config)
115
+ workflow_executor = clients.get_workflow_executor
116
+ workflow_client = clients.get_workflow_client
117
+
118
+ # Create and register workflow
119
+ workflow = create_rag_workflow(workflow_client, workflow_executor)
120
+ workflow_executor.register_workflow(workflow, overwrite: true)
121
+ puts "Registered workflow: #{workflow.name}"
122
+
123
+ # Sample document and question
124
+ sample_text = <<~TEXT
125
+ Ruby is a dynamic, open source programming language with a focus on simplicity#{' '}
126
+ and productivity. It has an elegant syntax that is natural to read and easy to write.
127
+ Ruby was created by Yukihiro "Matz" Matsumoto in the mid-1990s in Japan.
128
+ Ruby on Rails, often simply called Rails, is a web application framework written in Ruby.
129
+ Rails was created by David Heinemeier Hansson and released in 2004.
130
+ TEXT
131
+
132
+ question = 'Who created Ruby and when?'
133
+
134
+ puts
135
+ puts 'Executing RAG workflow...'
136
+ puts "Document: #{sample_text.length} characters"
137
+ puts "Question: #{question}"
138
+
139
+ result = workflow_executor.execute(
140
+ workflow.name,
141
+ input: {
142
+ 'text' => sample_text,
143
+ 'doc_id' => 'ruby_intro',
144
+ 'question' => question
145
+ },
146
+ wait_for_seconds: 60
147
+ )
148
+
149
+ puts
150
+ puts '-' * 70
151
+ puts "Status: #{result.status}"
152
+ puts "Answer: #{result.output['answer']}"
153
+ puts '-' * 70
154
+ puts
155
+ puts "View execution: #{config.ui_host}/execution/#{result.workflow_id}"
156
+ end
157
+
158
+ if __FILE__ == $PROGRAM_NAME
159
+ begin
160
+ main
161
+ rescue Conductor::ApiError => e
162
+ puts "API Error: #{e.message}"
163
+ rescue StandardError => e
164
+ puts "Error: #{e.message}"
165
+ puts e.backtrace.first(3).join("\n")
166
+ end
167
+ end
@@ -0,0 +1,244 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Schedule Management Journey - Comprehensive Example
5
+ #
6
+ # Demonstrates all Scheduler APIs through building an automated
7
+ # order processing system with scheduled workflows.
8
+ #
9
+ # APIs Covered:
10
+ # - save_schedule, get_schedule, get_all_schedules, delete_schedule
11
+ # - pause_schedule, resume_schedule, pause_all_schedules, resume_all_schedules
12
+ # - get_next_few_schedule_execution_times, search_schedule_executions
13
+ # - set_scheduler_tags, get_scheduler_tags, delete_scheduler_tags
14
+ #
15
+ # Usage:
16
+ # bundle exec ruby examples/schedule_journey.rb
17
+
18
+ require_relative '../lib/conductor'
19
+
20
+ class ScheduleJourney
21
+ def initialize
22
+ @config = Conductor::Configuration.new
23
+ @clients = Conductor::Orkes::OrkesClients.new(@config)
24
+ @scheduler = @clients.get_scheduler_client
25
+ @metadata = @clients.get_metadata_client
26
+ @created_schedules = []
27
+
28
+ puts '=' * 70
29
+ puts 'Schedule Management Journey'
30
+ puts '=' * 70
31
+ puts "Server: #{@config.server_url}"
32
+ puts
33
+ end
34
+
35
+ def run
36
+ setup_workflow
37
+ create_schedules
38
+ manage_schedules
39
+ work_with_tags
40
+ preview_and_search
41
+ cleanup
42
+ end
43
+
44
+ private
45
+
46
+ def setup_workflow
47
+ puts "\n--- Setting up test workflow ---"
48
+
49
+ # Create a simple workflow for scheduling
50
+ workflow_def = Conductor::Http::Models::WorkflowDef.new(
51
+ name: 'scheduled_order_processor',
52
+ version: 1,
53
+ description: 'Process orders on schedule',
54
+ tasks: [
55
+ {
56
+ 'name' => 'process_orders',
57
+ 'taskReferenceName' => 'process_ref',
58
+ 'type' => 'HTTP',
59
+ 'inputParameters' => {
60
+ 'http_request' => {
61
+ 'uri' => 'https://httpbin.org/post',
62
+ 'method' => 'POST',
63
+ 'body' => { 'timestamp' => '${workflow.input.timestamp}' }
64
+ }
65
+ }
66
+ }
67
+ ]
68
+ )
69
+
70
+ @metadata.register_workflow_def(workflow_def, overwrite: true)
71
+ puts "Created workflow: #{workflow_def.name}"
72
+ end
73
+
74
+ def create_schedules
75
+ puts "\n--- Creating Schedules ---"
76
+
77
+ # Schedule 1: Every minute (for demo)
78
+ schedule1 = {
79
+ 'name' => 'order_processor_minutely',
80
+ 'cronExpression' => '0 * * * * ?', # Every minute
81
+ 'startWorkflowRequest' => {
82
+ 'name' => 'scheduled_order_processor',
83
+ 'version' => 1,
84
+ 'input' => { 'type' => 'minutely' }
85
+ },
86
+ 'paused' => true, # Start paused for demo
87
+ 'scheduleStartTime' => (Time.now.to_i * 1000),
88
+ 'zoneId' => 'America/New_York'
89
+ }
90
+
91
+ @scheduler.save_schedule(schedule1)
92
+ @created_schedules << schedule1['name']
93
+ puts "Created schedule: #{schedule1['name']} (every minute)"
94
+
95
+ # Schedule 2: Daily at midnight
96
+ schedule2 = {
97
+ 'name' => 'order_processor_daily',
98
+ 'cronExpression' => '0 0 0 * * ?', # Daily at midnight
99
+ 'startWorkflowRequest' => {
100
+ 'name' => 'scheduled_order_processor',
101
+ 'version' => 1,
102
+ 'input' => { 'type' => 'daily' }
103
+ },
104
+ 'paused' => true
105
+ }
106
+
107
+ @scheduler.save_schedule(schedule2)
108
+ @created_schedules << schedule2['name']
109
+ puts "Created schedule: #{schedule2['name']} (daily at midnight)"
110
+
111
+ # Schedule 3: Weekly on Monday
112
+ schedule3 = {
113
+ 'name' => 'order_processor_weekly',
114
+ 'cronExpression' => '0 0 9 ? * MON', # Monday at 9 AM
115
+ 'startWorkflowRequest' => {
116
+ 'name' => 'scheduled_order_processor',
117
+ 'version' => 1,
118
+ 'input' => { 'type' => 'weekly' }
119
+ },
120
+ 'paused' => true
121
+ }
122
+
123
+ @scheduler.save_schedule(schedule3)
124
+ @created_schedules << schedule3['name']
125
+ puts "Created schedule: #{schedule3['name']} (weekly on Monday)"
126
+ end
127
+
128
+ def manage_schedules
129
+ puts "\n--- Managing Schedules ---"
130
+
131
+ # Get a specific schedule
132
+ schedule = @scheduler.get_schedule('order_processor_minutely')
133
+ puts "Retrieved schedule: #{schedule['name']}"
134
+ puts " Cron: #{schedule['cronExpression']}"
135
+ puts " Paused: #{schedule['paused']}"
136
+
137
+ # Get all schedules
138
+ all_schedules = @scheduler.get_all_schedules
139
+ puts "\nAll schedules (#{all_schedules.length} total):"
140
+ all_schedules.first(5).each do |s|
141
+ name = s.is_a?(Hash) ? s['name'] : s.name
142
+ puts " - #{name}"
143
+ end
144
+
145
+ # Pause and resume individual schedule
146
+ puts "\nPausing order_processor_minutely..."
147
+ @scheduler.pause_schedule('order_processor_minutely')
148
+
149
+ puts 'Resuming order_processor_minutely...'
150
+ begin
151
+ @scheduler.resume_schedule('order_processor_minutely')
152
+ rescue Conductor::ApiError => e
153
+ puts " Note: #{e.message}" if e.message.include?('404')
154
+ end
155
+
156
+ # Pause all schedules
157
+ puts "\nPausing all schedules..."
158
+ @scheduler.pause_all_schedules
159
+
160
+ # Resume all schedules
161
+ puts 'Resuming all schedules...'
162
+ @scheduler.resume_all_schedules
163
+ end
164
+
165
+ def work_with_tags
166
+ puts "\n--- Working with Tags ---"
167
+
168
+ tags = [
169
+ { 'key' => 'environment', 'value' => 'production' },
170
+ { 'key' => 'team', 'value' => 'orders' }
171
+ ]
172
+
173
+ # Set tags
174
+ puts 'Setting tags on order_processor_daily...'
175
+ @scheduler.set_scheduler_tags(tags, 'order_processor_daily')
176
+
177
+ # Get tags
178
+ retrieved_tags = @scheduler.get_scheduler_tags('order_processor_daily')
179
+ puts 'Retrieved tags:'
180
+ retrieved_tags.each do |tag|
181
+ key = tag.is_a?(Hash) ? tag['key'] : tag.key
182
+ value = tag.is_a?(Hash) ? tag['value'] : tag.value
183
+ puts " #{key}: #{value}"
184
+ end
185
+
186
+ # Delete tags
187
+ puts 'Deleting tags...'
188
+ @scheduler.delete_scheduler_tags(tags, 'order_processor_daily')
189
+ end
190
+
191
+ def preview_and_search
192
+ puts "\n--- Preview and Search ---"
193
+
194
+ # Get next execution times
195
+ puts 'Next 5 execution times for order_processor_minutely:'
196
+ begin
197
+ times = @scheduler.get_next_few_schedule_execution_times(
198
+ '0 * * * * ?',
199
+ limit: 5
200
+ )
201
+ times.first(5).each { |t| puts " - #{Time.at(t / 1000)}" }
202
+ rescue StandardError => e
203
+ puts " Could not get execution times: #{e.message}"
204
+ end
205
+
206
+ # Search executions
207
+ puts "\nSearching schedule executions..."
208
+ begin
209
+ results = @scheduler.search_schedule_executions(
210
+ start: 0,
211
+ size: 10,
212
+ query: '*:*'
213
+ )
214
+ count = results.is_a?(Hash) ? results['totalHits'] : results.total_hits
215
+ puts "Found #{count || 0} executions"
216
+ rescue StandardError => e
217
+ puts " Search returned: #{e.message}"
218
+ end
219
+ end
220
+
221
+ def cleanup
222
+ puts "\n--- Cleanup ---"
223
+
224
+ @created_schedules.each do |name|
225
+ @scheduler.delete_schedule(name)
226
+ puts "Deleted schedule: #{name}"
227
+ rescue StandardError => e
228
+ puts "Could not delete #{name}: #{e.message}"
229
+ end
230
+
231
+ puts "\nSchedule journey complete!"
232
+ end
233
+ end
234
+
235
+ if __FILE__ == $PROGRAM_NAME
236
+ begin
237
+ ScheduleJourney.new.run
238
+ rescue Conductor::ApiError => e
239
+ puts "API Error: #{e.message}"
240
+ rescue StandardError => e
241
+ puts "Error: #{e.message}"
242
+ puts e.backtrace.first(3).join("\n")
243
+ end
244
+ end