ruby_llm-agents 1.3.4 → 2.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.
- checksums.yaml +4 -4
- data/README.md +112 -336
- data/app/controllers/concerns/ruby_llm/agents/sortable.rb +0 -1
- data/app/controllers/ruby_llm/agents/agents_controller.rb +5 -56
- data/app/controllers/ruby_llm/agents/dashboard_controller.rb +22 -106
- data/app/controllers/ruby_llm/agents/executions_controller.rb +4 -114
- data/app/controllers/ruby_llm/agents/tenants_controller.rb +30 -2
- data/app/helpers/ruby_llm/agents/application_helper.rb +19 -53
- data/app/models/ruby_llm/agents/execution/analytics.rb +13 -54
- data/app/models/ruby_llm/agents/execution/scopes.rb +61 -14
- data/app/models/ruby_llm/agents/execution.rb +52 -12
- data/app/models/ruby_llm/agents/execution_detail.rb +18 -0
- data/app/models/ruby_llm/agents/tenant/budgetable.rb +132 -24
- data/app/models/ruby_llm/agents/tenant/incrementable.rb +117 -0
- data/app/models/ruby_llm/agents/tenant/resettable.rb +128 -0
- data/app/models/ruby_llm/agents/tenant/trackable.rb +46 -12
- data/app/models/ruby_llm/agents/tenant.rb +2 -3
- data/app/models/ruby_llm/agents/tenant_budget.rb +6 -3
- data/app/services/ruby_llm/agents/agent_registry.rb +6 -112
- data/app/views/layouts/ruby_llm/agents/application.html.erb +89 -252
- data/app/views/ruby_llm/agents/agents/_config_agent.html.erb +71 -218
- data/app/views/ruby_llm/agents/agents/_config_embedder.html.erb +20 -63
- data/app/views/ruby_llm/agents/agents/_config_image_generator.html.erb +44 -131
- data/app/views/ruby_llm/agents/agents/_config_moderator.html.erb +16 -57
- data/app/views/ruby_llm/agents/agents/_config_speaker.html.erb +39 -104
- data/app/views/ruby_llm/agents/agents/_config_transcriber.html.erb +29 -82
- data/app/views/ruby_llm/agents/agents/_empty_state.html.erb +4 -14
- data/app/views/ruby_llm/agents/agents/index.html.erb +105 -274
- data/app/views/ruby_llm/agents/agents/show.html.erb +248 -378
- data/app/views/ruby_llm/agents/dashboard/_action_center.html.erb +29 -52
- data/app/views/ruby_llm/agents/dashboard/_tenant_budget.html.erb +73 -99
- data/app/views/ruby_llm/agents/dashboard/index.html.erb +228 -433
- data/app/views/ruby_llm/agents/executions/_execution.html.erb +1 -1
- data/app/views/ruby_llm/agents/executions/_filters.html.erb +4 -25
- data/app/views/ruby_llm/agents/executions/_list.html.erb +111 -152
- data/app/views/ruby_llm/agents/executions/index.html.erb +5 -7
- data/app/views/ruby_llm/agents/executions/show.html.erb +526 -1037
- data/app/views/ruby_llm/agents/shared/_agent_type_badge.html.erb +5 -21
- data/app/views/ruby_llm/agents/shared/_executions_table.html.erb +70 -191
- data/app/views/ruby_llm/agents/shared/_filter_dropdown.html.erb +16 -44
- data/app/views/ruby_llm/agents/shared/_select_dropdown.html.erb +12 -41
- data/app/views/ruby_llm/agents/shared/_status_badge.html.erb +11 -65
- data/app/views/ruby_llm/agents/shared/_tenant_filter.html.erb +6 -5
- data/app/views/ruby_llm/agents/system_config/show.html.erb +240 -351
- data/app/views/ruby_llm/agents/tenants/_form.html.erb +67 -77
- data/app/views/ruby_llm/agents/tenants/edit.html.erb +7 -9
- data/app/views/ruby_llm/agents/tenants/index.html.erb +100 -122
- data/app/views/ruby_llm/agents/tenants/show.html.erb +146 -336
- data/config/routes.rb +0 -13
- data/lib/generators/ruby_llm_agents/install_generator.rb +13 -17
- data/lib/generators/ruby_llm_agents/migrate_structure_generator.rb +2 -12
- data/lib/generators/ruby_llm_agents/restructure_generator.rb +0 -2
- data/lib/generators/ruby_llm_agents/templates/add_usage_counters_to_tenants_migration.rb.tt +37 -0
- data/lib/generators/ruby_llm_agents/templates/agent.rb.tt +1 -2
- data/lib/generators/ruby_llm_agents/templates/application_agent.rb.tt +1 -1
- data/lib/generators/ruby_llm_agents/templates/application_image_pipeline.rb.tt +0 -1
- data/lib/generators/ruby_llm_agents/templates/create_execution_details_migration.rb.tt +27 -0
- data/lib/generators/ruby_llm_agents/templates/create_tenants_migration.rb.tt +25 -0
- data/lib/generators/ruby_llm_agents/templates/image_pipeline.rb.tt +0 -1
- data/lib/generators/ruby_llm_agents/templates/initializer.rb.tt +33 -12
- data/lib/generators/ruby_llm_agents/templates/migration.rb.tt +40 -71
- data/lib/generators/ruby_llm_agents/templates/remove_agent_version_migration.rb.tt +13 -0
- data/lib/generators/ruby_llm_agents/templates/remove_workflow_columns_migration.rb.tt +19 -0
- data/lib/generators/ruby_llm_agents/templates/skills/AGENTS.md.tt +2 -4
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_PIPELINES.md.tt +0 -1
- data/lib/generators/ruby_llm_agents/templates/split_execution_details_migration.rb.tt +232 -0
- data/lib/generators/ruby_llm_agents/upgrade_generator.rb +77 -259
- data/lib/ruby_llm/agents/audio/speaker.rb +0 -1
- data/lib/ruby_llm/agents/audio/transcriber.rb +0 -1
- data/lib/ruby_llm/agents/base_agent.rb +54 -23
- data/lib/ruby_llm/agents/core/base/callbacks.rb +142 -0
- data/lib/ruby_llm/agents/core/base.rb +23 -55
- data/lib/ruby_llm/agents/core/configuration.rb +97 -117
- data/lib/ruby_llm/agents/core/errors.rb +0 -58
- data/lib/ruby_llm/agents/core/instrumentation.rb +157 -110
- data/lib/ruby_llm/agents/core/llm_tenant.rb +8 -7
- data/lib/ruby_llm/agents/core/version.rb +1 -1
- data/lib/ruby_llm/agents/dsl/base.rb +157 -17
- data/lib/ruby_llm/agents/dsl/caching.rb +33 -2
- data/lib/ruby_llm/agents/dsl/reliability.rb +148 -0
- data/lib/ruby_llm/agents/dsl.rb +1 -2
- data/lib/ruby_llm/agents/image/analyzer/execution.rb +1 -2
- data/lib/ruby_llm/agents/image/background_remover/execution.rb +1 -2
- data/lib/ruby_llm/agents/image/concerns/image_operation_dsl.rb +1 -13
- data/lib/ruby_llm/agents/image/concerns/image_operation_execution.rb +2 -2
- data/lib/ruby_llm/agents/image/editor/dsl.rb +0 -14
- data/lib/ruby_llm/agents/image/editor/execution.rb +1 -10
- data/lib/ruby_llm/agents/image/editor.rb +0 -1
- data/lib/ruby_llm/agents/image/generator.rb +0 -21
- data/lib/ruby_llm/agents/image/pipeline/dsl.rb +0 -13
- data/lib/ruby_llm/agents/image/pipeline/execution.rb +0 -1
- data/lib/ruby_llm/agents/image/transformer/dsl.rb +0 -13
- data/lib/ruby_llm/agents/image/transformer/execution.rb +1 -10
- data/lib/ruby_llm/agents/image/transformer.rb +0 -1
- data/lib/ruby_llm/agents/image/upscaler/execution.rb +1 -2
- data/lib/ruby_llm/agents/image/variator/execution.rb +1 -2
- data/lib/ruby_llm/agents/infrastructure/alert_manager.rb +78 -173
- data/lib/ruby_llm/agents/infrastructure/budget/budget_query.rb +66 -2
- data/lib/ruby_llm/agents/infrastructure/budget/spend_recorder.rb +0 -12
- data/lib/ruby_llm/agents/infrastructure/circuit_breaker.rb +10 -13
- data/lib/ruby_llm/agents/infrastructure/execution_logger_job.rb +8 -0
- data/lib/ruby_llm/agents/pipeline/context.rb +0 -1
- data/lib/ruby_llm/agents/pipeline/middleware/budget.rb +28 -4
- data/lib/ruby_llm/agents/pipeline/middleware/cache.rb +3 -10
- data/lib/ruby_llm/agents/pipeline/middleware/instrumentation.rb +88 -55
- data/lib/ruby_llm/agents/pipeline/middleware/tenant.rb +5 -41
- data/lib/ruby_llm/agents/rails/engine.rb +6 -6
- data/lib/ruby_llm/agents/results/base.rb +1 -49
- data/lib/ruby_llm/agents/text/embedder.rb +0 -1
- data/lib/ruby_llm/agents.rb +1 -9
- data/lib/tasks/ruby_llm_agents.rake +34 -0
- metadata +14 -83
- data/app/controllers/ruby_llm/agents/api_configurations_controller.rb +0 -214
- data/app/controllers/ruby_llm/agents/workflows_controller.rb +0 -544
- data/app/mailers/ruby_llm/agents/alert_mailer.rb +0 -84
- data/app/mailers/ruby_llm/agents/application_mailer.rb +0 -28
- data/app/models/ruby_llm/agents/api_configuration.rb +0 -386
- data/app/models/ruby_llm/agents/execution/workflow.rb +0 -170
- data/app/models/ruby_llm/agents/tenant/configurable.rb +0 -135
- data/app/views/ruby_llm/agents/agents/_agent.html.erb +0 -98
- data/app/views/ruby_llm/agents/agents/_version_comparison.html.erb +0 -186
- data/app/views/ruby_llm/agents/agents/_workflow.html.erb +0 -126
- data/app/views/ruby_llm/agents/alert_mailer/alert_notification.html.erb +0 -107
- data/app/views/ruby_llm/agents/alert_mailer/alert_notification.text.erb +0 -18
- data/app/views/ruby_llm/agents/api_configurations/_api_key_field.html.erb +0 -34
- data/app/views/ruby_llm/agents/api_configurations/_form.html.erb +0 -288
- data/app/views/ruby_llm/agents/api_configurations/edit.html.erb +0 -95
- data/app/views/ruby_llm/agents/api_configurations/edit_tenant.html.erb +0 -97
- data/app/views/ruby_llm/agents/api_configurations/show.html.erb +0 -214
- data/app/views/ruby_llm/agents/api_configurations/tenant.html.erb +0 -179
- data/app/views/ruby_llm/agents/dashboard/_agent_comparison.html.erb +0 -73
- data/app/views/ruby_llm/agents/dashboard/_alerts_feed.html.erb +0 -62
- data/app/views/ruby_llm/agents/dashboard/_breaker_strip.html.erb +0 -47
- data/app/views/ruby_llm/agents/dashboard/_budgets_bar.html.erb +0 -75
- data/app/views/ruby_llm/agents/dashboard/_model_comparison.html.erb +0 -56
- data/app/views/ruby_llm/agents/dashboard/_model_cost_breakdown.html.erb +0 -115
- data/app/views/ruby_llm/agents/dashboard/_now_strip.html.erb +0 -59
- data/app/views/ruby_llm/agents/dashboard/_top_errors.html.erb +0 -60
- data/app/views/ruby_llm/agents/executions/_workflow_summary.html.erb +0 -86
- data/app/views/ruby_llm/agents/executions/dry_run.html.erb +0 -149
- data/app/views/ruby_llm/agents/shared/_breadcrumbs.html.erb +0 -48
- data/app/views/ruby_llm/agents/shared/_nav_link.html.erb +0 -27
- data/app/views/ruby_llm/agents/shared/_stat_card.html.erb +0 -14
- data/app/views/ruby_llm/agents/shared/_workflow_type_badge.html.erb +0 -35
- data/app/views/ruby_llm/agents/workflows/_empty_state.html.erb +0 -22
- data/app/views/ruby_llm/agents/workflows/_step_performance.html.erb +0 -228
- data/app/views/ruby_llm/agents/workflows/_structure_dsl.html.erb +0 -539
- data/app/views/ruby_llm/agents/workflows/_structure_parallel.html.erb +0 -76
- data/app/views/ruby_llm/agents/workflows/_structure_pipeline.html.erb +0 -74
- data/app/views/ruby_llm/agents/workflows/_structure_router.html.erb +0 -108
- data/app/views/ruby_llm/agents/workflows/_workflow_diagram.html.erb +0 -920
- data/app/views/ruby_llm/agents/workflows/index.html.erb +0 -179
- data/app/views/ruby_llm/agents/workflows/show.html.erb +0 -467
- data/lib/generators/ruby_llm_agents/api_configuration_generator.rb +0 -100
- data/lib/generators/ruby_llm_agents/templates/add_workflow_migration.rb.tt +0 -38
- data/lib/generators/ruby_llm_agents/templates/application_workflow.rb.tt +0 -48
- data/lib/generators/ruby_llm_agents/templates/create_api_configurations_migration.rb.tt +0 -90
- data/lib/generators/ruby_llm_agents/templates/skills/WORKFLOWS.md.tt +0 -551
- data/lib/ruby_llm/agents/core/base/moderation_dsl.rb +0 -181
- data/lib/ruby_llm/agents/core/base/moderation_execution.rb +0 -274
- data/lib/ruby_llm/agents/core/resolved_config.rb +0 -348
- data/lib/ruby_llm/agents/image/generator/content_policy.rb +0 -95
- data/lib/ruby_llm/agents/infrastructure/redactor.rb +0 -130
- data/lib/ruby_llm/agents/results/moderation_result.rb +0 -158
- data/lib/ruby_llm/agents/text/moderator.rb +0 -237
- data/lib/ruby_llm/agents/workflow/approval.rb +0 -205
- data/lib/ruby_llm/agents/workflow/approval_store.rb +0 -179
- data/lib/ruby_llm/agents/workflow/async.rb +0 -220
- data/lib/ruby_llm/agents/workflow/async_executor.rb +0 -156
- data/lib/ruby_llm/agents/workflow/dsl/executor.rb +0 -467
- data/lib/ruby_llm/agents/workflow/dsl/input_schema.rb +0 -244
- data/lib/ruby_llm/agents/workflow/dsl/iteration_executor.rb +0 -289
- data/lib/ruby_llm/agents/workflow/dsl/parallel_group.rb +0 -107
- data/lib/ruby_llm/agents/workflow/dsl/route_builder.rb +0 -150
- data/lib/ruby_llm/agents/workflow/dsl/schedule_helpers.rb +0 -187
- data/lib/ruby_llm/agents/workflow/dsl/step_config.rb +0 -352
- data/lib/ruby_llm/agents/workflow/dsl/step_executor.rb +0 -415
- data/lib/ruby_llm/agents/workflow/dsl/wait_config.rb +0 -257
- data/lib/ruby_llm/agents/workflow/dsl/wait_executor.rb +0 -317
- data/lib/ruby_llm/agents/workflow/dsl.rb +0 -576
- data/lib/ruby_llm/agents/workflow/instrumentation.rb +0 -249
- data/lib/ruby_llm/agents/workflow/notifiers/base.rb +0 -117
- data/lib/ruby_llm/agents/workflow/notifiers/email.rb +0 -117
- data/lib/ruby_llm/agents/workflow/notifiers/slack.rb +0 -180
- data/lib/ruby_llm/agents/workflow/notifiers/webhook.rb +0 -121
- data/lib/ruby_llm/agents/workflow/notifiers.rb +0 -70
- data/lib/ruby_llm/agents/workflow/orchestrator.rb +0 -416
- data/lib/ruby_llm/agents/workflow/result.rb +0 -592
- data/lib/ruby_llm/agents/workflow/thread_pool.rb +0 -185
- data/lib/ruby_llm/agents/workflow/throttle_manager.rb +0 -206
- data/lib/ruby_llm/agents/workflow/wait_result.rb +0 -213
|
@@ -1,592 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module RubyLLM
|
|
4
|
-
module Agents
|
|
5
|
-
class Workflow
|
|
6
|
-
# Result wrapper for workflow executions with aggregate metrics
|
|
7
|
-
#
|
|
8
|
-
# Extends the base Result class with workflow-specific data including
|
|
9
|
-
# step results, branch results, routing information, and aggregated
|
|
10
|
-
# token/cost metrics across all child executions.
|
|
11
|
-
#
|
|
12
|
-
# @example Pipeline result
|
|
13
|
-
# result = ContentPipeline.call(text: "input")
|
|
14
|
-
# result.content # Final output
|
|
15
|
-
# result.steps[:extract] # Individual step result
|
|
16
|
-
# result.total_cost # Sum of all steps
|
|
17
|
-
#
|
|
18
|
-
# @example Parallel result
|
|
19
|
-
# result = ReviewAnalyzer.call(text: "review")
|
|
20
|
-
# result.branches[:sentiment] # Branch result
|
|
21
|
-
# result.failed_branches # [:toxicity] if it failed
|
|
22
|
-
#
|
|
23
|
-
# @example Router result
|
|
24
|
-
# result = SupportRouter.call(message: "billing issue")
|
|
25
|
-
# result.routed_to # :billing
|
|
26
|
-
# result.classification # Classification details
|
|
27
|
-
#
|
|
28
|
-
# @api public
|
|
29
|
-
class Result
|
|
30
|
-
extend ActiveSupport::Delegation
|
|
31
|
-
|
|
32
|
-
# @!attribute [r] content
|
|
33
|
-
# @return [Object] The final processed content
|
|
34
|
-
attr_reader :content
|
|
35
|
-
|
|
36
|
-
# @!attribute [r] workflow_type
|
|
37
|
-
# @return [String] The workflow class name
|
|
38
|
-
attr_reader :workflow_type
|
|
39
|
-
|
|
40
|
-
# @!attribute [r] workflow_id
|
|
41
|
-
# @return [String] Unique identifier for this workflow execution
|
|
42
|
-
attr_reader :workflow_id
|
|
43
|
-
|
|
44
|
-
# @!group Step/Branch Results
|
|
45
|
-
|
|
46
|
-
# @!attribute [r] steps
|
|
47
|
-
# @return [Hash<Symbol, Result>] Results from pipeline steps
|
|
48
|
-
attr_reader :steps
|
|
49
|
-
|
|
50
|
-
# @!attribute [r] branches
|
|
51
|
-
# @return [Hash<Symbol, Result>] Results from parallel branches
|
|
52
|
-
attr_reader :branches
|
|
53
|
-
|
|
54
|
-
# @!endgroup
|
|
55
|
-
|
|
56
|
-
# @!group Router Results
|
|
57
|
-
|
|
58
|
-
# @!attribute [r] routed_to
|
|
59
|
-
# @return [Symbol, nil] The route that was selected
|
|
60
|
-
attr_reader :routed_to
|
|
61
|
-
|
|
62
|
-
# @!attribute [r] classification
|
|
63
|
-
# @return [Hash, nil] Classification details from router
|
|
64
|
-
attr_reader :classification
|
|
65
|
-
|
|
66
|
-
# @!attribute [r] classifier_result
|
|
67
|
-
# @return [Result, nil] The classifier agent's result
|
|
68
|
-
attr_reader :classifier_result
|
|
69
|
-
|
|
70
|
-
# @!endgroup
|
|
71
|
-
|
|
72
|
-
# @!group Timing
|
|
73
|
-
|
|
74
|
-
# @!attribute [r] started_at
|
|
75
|
-
# @return [Time] When the workflow started
|
|
76
|
-
attr_reader :started_at
|
|
77
|
-
|
|
78
|
-
# @!attribute [r] completed_at
|
|
79
|
-
# @return [Time] When the workflow completed
|
|
80
|
-
attr_reader :completed_at
|
|
81
|
-
|
|
82
|
-
# @!attribute [r] duration_ms
|
|
83
|
-
# @return [Integer] Total workflow duration in milliseconds
|
|
84
|
-
attr_reader :duration_ms
|
|
85
|
-
|
|
86
|
-
# @!endgroup
|
|
87
|
-
|
|
88
|
-
# @!group Status
|
|
89
|
-
|
|
90
|
-
# @!attribute [r] status
|
|
91
|
-
# @return [String] Workflow status: "success", "error", "partial"
|
|
92
|
-
attr_reader :status
|
|
93
|
-
|
|
94
|
-
# @!attribute [r] error_class
|
|
95
|
-
# @return [String, nil] Error class if failed
|
|
96
|
-
attr_reader :error_class
|
|
97
|
-
|
|
98
|
-
# @!attribute [r] error_message
|
|
99
|
-
# @return [String, nil] Error message if failed
|
|
100
|
-
attr_reader :error_message
|
|
101
|
-
|
|
102
|
-
# @!attribute [r] errors
|
|
103
|
-
# @return [Hash<Symbol, Exception>] Errors by step/branch name
|
|
104
|
-
attr_reader :errors
|
|
105
|
-
|
|
106
|
-
# @!endgroup
|
|
107
|
-
|
|
108
|
-
# Creates a new WorkflowResult
|
|
109
|
-
#
|
|
110
|
-
# @param content [Object] The final processed content
|
|
111
|
-
# @param options [Hash] Additional result metadata
|
|
112
|
-
def initialize(content:, **options)
|
|
113
|
-
@content = content
|
|
114
|
-
@workflow_type = options[:workflow_type]
|
|
115
|
-
@workflow_id = options[:workflow_id]
|
|
116
|
-
|
|
117
|
-
# Step/branch results
|
|
118
|
-
@steps = options[:steps] || {}
|
|
119
|
-
@branches = options[:branches] || {}
|
|
120
|
-
|
|
121
|
-
# Router results
|
|
122
|
-
@routed_to = options[:routed_to]
|
|
123
|
-
@classification = options[:classification]
|
|
124
|
-
@classifier_result = options[:classifier_result]
|
|
125
|
-
|
|
126
|
-
# Timing
|
|
127
|
-
@started_at = options[:started_at]
|
|
128
|
-
@completed_at = options[:completed_at]
|
|
129
|
-
@duration_ms = options[:duration_ms]
|
|
130
|
-
|
|
131
|
-
# Status
|
|
132
|
-
@status = options[:status] || "success"
|
|
133
|
-
@error_class = options[:error_class]
|
|
134
|
-
@error_message = options[:error_message]
|
|
135
|
-
@errors = options[:errors] || {}
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
# Returns all child results (steps + branches + classifier)
|
|
139
|
-
#
|
|
140
|
-
# @return [Array<Result>] All child results
|
|
141
|
-
def child_results
|
|
142
|
-
results = []
|
|
143
|
-
results.concat(steps.values) if steps.any?
|
|
144
|
-
results.concat(branches.values) if branches.any?
|
|
145
|
-
results << classifier_result if classifier_result
|
|
146
|
-
results.compact
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
# @!group Aggregate Metrics
|
|
150
|
-
|
|
151
|
-
# Returns total input tokens across all child executions
|
|
152
|
-
#
|
|
153
|
-
# @return [Integer] Total input tokens
|
|
154
|
-
def input_tokens
|
|
155
|
-
child_results.sum { |r| r.input_tokens || 0 }
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
# Returns total output tokens across all child executions
|
|
159
|
-
#
|
|
160
|
-
# @return [Integer] Total output tokens
|
|
161
|
-
def output_tokens
|
|
162
|
-
child_results.sum { |r| r.output_tokens || 0 }
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
# Returns total tokens across all child executions
|
|
166
|
-
#
|
|
167
|
-
# @return [Integer] Total tokens
|
|
168
|
-
def total_tokens
|
|
169
|
-
input_tokens + output_tokens
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
# Returns total cached tokens across all child executions
|
|
173
|
-
#
|
|
174
|
-
# @return [Integer] Total cached tokens
|
|
175
|
-
def cached_tokens
|
|
176
|
-
child_results.sum { |r| r.cached_tokens || 0 }
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
# Returns total input cost across all child executions
|
|
180
|
-
#
|
|
181
|
-
# @return [Float] Total input cost in USD
|
|
182
|
-
def input_cost
|
|
183
|
-
child_results.sum { |r| r.input_cost || 0.0 }
|
|
184
|
-
end
|
|
185
|
-
|
|
186
|
-
# Returns total output cost across all child executions
|
|
187
|
-
#
|
|
188
|
-
# @return [Float] Total output cost in USD
|
|
189
|
-
def output_cost
|
|
190
|
-
child_results.sum { |r| r.output_cost || 0.0 }
|
|
191
|
-
end
|
|
192
|
-
|
|
193
|
-
# Returns total cost across all child executions
|
|
194
|
-
#
|
|
195
|
-
# @return [Float] Total cost in USD
|
|
196
|
-
def total_cost
|
|
197
|
-
child_results.sum { |r| r.total_cost || 0.0 }
|
|
198
|
-
end
|
|
199
|
-
|
|
200
|
-
# Returns classification cost (router workflows only)
|
|
201
|
-
#
|
|
202
|
-
# @return [Float] Classification cost in USD
|
|
203
|
-
def classification_cost
|
|
204
|
-
classifier_result&.total_cost || 0.0
|
|
205
|
-
end
|
|
206
|
-
|
|
207
|
-
# @!endgroup
|
|
208
|
-
|
|
209
|
-
# @!group Status Helpers
|
|
210
|
-
|
|
211
|
-
# Returns whether the workflow succeeded
|
|
212
|
-
#
|
|
213
|
-
# @return [Boolean] true if status is "success"
|
|
214
|
-
def success?
|
|
215
|
-
status == "success"
|
|
216
|
-
end
|
|
217
|
-
|
|
218
|
-
# Returns whether the workflow failed
|
|
219
|
-
#
|
|
220
|
-
# @return [Boolean] true if status is "error"
|
|
221
|
-
def error?
|
|
222
|
-
status == "error"
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
# Returns whether the workflow partially succeeded
|
|
226
|
-
#
|
|
227
|
-
# @return [Boolean] true if status is "partial"
|
|
228
|
-
def partial?
|
|
229
|
-
status == "partial"
|
|
230
|
-
end
|
|
231
|
-
|
|
232
|
-
# @!endgroup
|
|
233
|
-
|
|
234
|
-
# @!group Pipeline Helpers
|
|
235
|
-
|
|
236
|
-
# Returns whether all pipeline steps succeeded
|
|
237
|
-
#
|
|
238
|
-
# @return [Boolean] true if all steps successful
|
|
239
|
-
def all_steps_successful?
|
|
240
|
-
return true if steps.empty?
|
|
241
|
-
|
|
242
|
-
steps.values.all? { |r| r.respond_to?(:success?) ? r.success? : true }
|
|
243
|
-
end
|
|
244
|
-
|
|
245
|
-
# Returns the names of failed steps
|
|
246
|
-
#
|
|
247
|
-
# @return [Array<Symbol>] Failed step names
|
|
248
|
-
def failed_steps
|
|
249
|
-
steps.select { |_, r| r.respond_to?(:error?) && r.error? }.keys
|
|
250
|
-
end
|
|
251
|
-
|
|
252
|
-
# Returns the names of skipped steps
|
|
253
|
-
#
|
|
254
|
-
# @return [Array<Symbol>] Skipped step names
|
|
255
|
-
def skipped_steps
|
|
256
|
-
steps.select { |_, r| r.respond_to?(:skipped?) && r.skipped? }.keys
|
|
257
|
-
end
|
|
258
|
-
|
|
259
|
-
# @!endgroup
|
|
260
|
-
|
|
261
|
-
# @!group Parallel Helpers
|
|
262
|
-
|
|
263
|
-
# Returns whether all parallel branches succeeded
|
|
264
|
-
#
|
|
265
|
-
# @return [Boolean] true if all branches successful
|
|
266
|
-
def all_branches_successful?
|
|
267
|
-
return true if branches.empty?
|
|
268
|
-
|
|
269
|
-
branches.values.all? { |r| r.nil? || (r.respond_to?(:success?) ? r.success? : true) }
|
|
270
|
-
end
|
|
271
|
-
|
|
272
|
-
# Returns the names of failed branches
|
|
273
|
-
#
|
|
274
|
-
# @return [Array<Symbol>] Failed branch names
|
|
275
|
-
def failed_branches
|
|
276
|
-
failed = branches.select { |_, r| r.respond_to?(:error?) && r.error? }.keys
|
|
277
|
-
failed += errors.keys
|
|
278
|
-
failed.uniq
|
|
279
|
-
end
|
|
280
|
-
|
|
281
|
-
# Returns the names of successful branches
|
|
282
|
-
#
|
|
283
|
-
# @return [Array<Symbol>] Successful branch names
|
|
284
|
-
def successful_branches
|
|
285
|
-
branches.select { |_, r| r.respond_to?(:success?) && r.success? }.keys
|
|
286
|
-
end
|
|
287
|
-
|
|
288
|
-
# @!endgroup
|
|
289
|
-
|
|
290
|
-
# Converts the result to a hash
|
|
291
|
-
#
|
|
292
|
-
# @return [Hash] All result data
|
|
293
|
-
def to_h
|
|
294
|
-
{
|
|
295
|
-
content: content,
|
|
296
|
-
workflow_type: workflow_type,
|
|
297
|
-
workflow_id: workflow_id,
|
|
298
|
-
status: status,
|
|
299
|
-
steps: steps.transform_values { |r| r.respond_to?(:to_h) ? r.to_h : r },
|
|
300
|
-
branches: branches.transform_values { |r| r.respond_to?(:to_h) ? r.to_h : r },
|
|
301
|
-
routed_to: routed_to,
|
|
302
|
-
classification: classification,
|
|
303
|
-
input_tokens: input_tokens,
|
|
304
|
-
output_tokens: output_tokens,
|
|
305
|
-
total_tokens: total_tokens,
|
|
306
|
-
cached_tokens: cached_tokens,
|
|
307
|
-
input_cost: input_cost,
|
|
308
|
-
output_cost: output_cost,
|
|
309
|
-
total_cost: total_cost,
|
|
310
|
-
started_at: started_at,
|
|
311
|
-
completed_at: completed_at,
|
|
312
|
-
duration_ms: duration_ms,
|
|
313
|
-
error_class: error_class,
|
|
314
|
-
error_message: error_message,
|
|
315
|
-
errors: errors.transform_values { |e| { class: e.class.name, message: e.message } }
|
|
316
|
-
}
|
|
317
|
-
end
|
|
318
|
-
|
|
319
|
-
# Delegate hash methods to content for convenience
|
|
320
|
-
delegate :[], :dig, :keys, :values, :each, :map, to: :content, allow_nil: true
|
|
321
|
-
|
|
322
|
-
# Custom to_json that includes workflow metadata
|
|
323
|
-
#
|
|
324
|
-
# @param args [Array] Arguments passed to to_json
|
|
325
|
-
# @return [String] JSON representation
|
|
326
|
-
def to_json(*args)
|
|
327
|
-
to_h.to_json(*args)
|
|
328
|
-
end
|
|
329
|
-
end
|
|
330
|
-
|
|
331
|
-
# Represents a skipped step result
|
|
332
|
-
class SkippedResult
|
|
333
|
-
attr_reader :step_name, :reason
|
|
334
|
-
|
|
335
|
-
def initialize(step_name, reason: nil)
|
|
336
|
-
@step_name = step_name
|
|
337
|
-
@reason = reason
|
|
338
|
-
end
|
|
339
|
-
|
|
340
|
-
def content
|
|
341
|
-
nil
|
|
342
|
-
end
|
|
343
|
-
|
|
344
|
-
def success?
|
|
345
|
-
true
|
|
346
|
-
end
|
|
347
|
-
|
|
348
|
-
def error?
|
|
349
|
-
false
|
|
350
|
-
end
|
|
351
|
-
|
|
352
|
-
def skipped?
|
|
353
|
-
true
|
|
354
|
-
end
|
|
355
|
-
|
|
356
|
-
def input_tokens
|
|
357
|
-
0
|
|
358
|
-
end
|
|
359
|
-
|
|
360
|
-
def output_tokens
|
|
361
|
-
0
|
|
362
|
-
end
|
|
363
|
-
|
|
364
|
-
def total_tokens
|
|
365
|
-
0
|
|
366
|
-
end
|
|
367
|
-
|
|
368
|
-
def cached_tokens
|
|
369
|
-
0
|
|
370
|
-
end
|
|
371
|
-
|
|
372
|
-
def input_cost
|
|
373
|
-
0.0
|
|
374
|
-
end
|
|
375
|
-
|
|
376
|
-
def output_cost
|
|
377
|
-
0.0
|
|
378
|
-
end
|
|
379
|
-
|
|
380
|
-
def total_cost
|
|
381
|
-
0.0
|
|
382
|
-
end
|
|
383
|
-
|
|
384
|
-
def to_h
|
|
385
|
-
{ skipped: true, step_name: step_name, reason: reason }
|
|
386
|
-
end
|
|
387
|
-
end
|
|
388
|
-
|
|
389
|
-
# Result wrapper for sub-workflow execution
|
|
390
|
-
#
|
|
391
|
-
# Wraps a nested workflow result while providing access to
|
|
392
|
-
# aggregate metrics and the underlying workflow result.
|
|
393
|
-
#
|
|
394
|
-
# @api public
|
|
395
|
-
class SubWorkflowResult
|
|
396
|
-
attr_reader :content, :sub_workflow_result, :workflow_type, :step_name
|
|
397
|
-
|
|
398
|
-
def initialize(content:, sub_workflow_result:, workflow_type:, step_name:)
|
|
399
|
-
@content = content
|
|
400
|
-
@sub_workflow_result = sub_workflow_result
|
|
401
|
-
@workflow_type = workflow_type
|
|
402
|
-
@step_name = step_name
|
|
403
|
-
end
|
|
404
|
-
|
|
405
|
-
def success?
|
|
406
|
-
sub_workflow_result.respond_to?(:success?) ? sub_workflow_result.success? : true
|
|
407
|
-
end
|
|
408
|
-
|
|
409
|
-
def error?
|
|
410
|
-
sub_workflow_result.respond_to?(:error?) ? sub_workflow_result.error? : false
|
|
411
|
-
end
|
|
412
|
-
|
|
413
|
-
def skipped?
|
|
414
|
-
false
|
|
415
|
-
end
|
|
416
|
-
|
|
417
|
-
# Delegate metrics to sub-workflow result
|
|
418
|
-
def input_tokens
|
|
419
|
-
sub_workflow_result.respond_to?(:input_tokens) ? sub_workflow_result.input_tokens : 0
|
|
420
|
-
end
|
|
421
|
-
|
|
422
|
-
def output_tokens
|
|
423
|
-
sub_workflow_result.respond_to?(:output_tokens) ? sub_workflow_result.output_tokens : 0
|
|
424
|
-
end
|
|
425
|
-
|
|
426
|
-
def total_tokens
|
|
427
|
-
input_tokens + output_tokens
|
|
428
|
-
end
|
|
429
|
-
|
|
430
|
-
def cached_tokens
|
|
431
|
-
sub_workflow_result.respond_to?(:cached_tokens) ? sub_workflow_result.cached_tokens : 0
|
|
432
|
-
end
|
|
433
|
-
|
|
434
|
-
def input_cost
|
|
435
|
-
sub_workflow_result.respond_to?(:input_cost) ? sub_workflow_result.input_cost : 0.0
|
|
436
|
-
end
|
|
437
|
-
|
|
438
|
-
def output_cost
|
|
439
|
-
sub_workflow_result.respond_to?(:output_cost) ? sub_workflow_result.output_cost : 0.0
|
|
440
|
-
end
|
|
441
|
-
|
|
442
|
-
def total_cost
|
|
443
|
-
sub_workflow_result.respond_to?(:total_cost) ? sub_workflow_result.total_cost : 0.0
|
|
444
|
-
end
|
|
445
|
-
|
|
446
|
-
# Access sub-workflow steps
|
|
447
|
-
def steps
|
|
448
|
-
sub_workflow_result.respond_to?(:steps) ? sub_workflow_result.steps : {}
|
|
449
|
-
end
|
|
450
|
-
|
|
451
|
-
def to_h
|
|
452
|
-
{
|
|
453
|
-
content: content,
|
|
454
|
-
workflow_type: workflow_type,
|
|
455
|
-
step_name: step_name,
|
|
456
|
-
sub_workflow: sub_workflow_result.respond_to?(:to_h) ? sub_workflow_result.to_h : sub_workflow_result,
|
|
457
|
-
input_tokens: input_tokens,
|
|
458
|
-
output_tokens: output_tokens,
|
|
459
|
-
total_cost: total_cost
|
|
460
|
-
}
|
|
461
|
-
end
|
|
462
|
-
|
|
463
|
-
# Delegate hash access to content
|
|
464
|
-
def [](key)
|
|
465
|
-
content.is_a?(Hash) ? content[key] : nil
|
|
466
|
-
end
|
|
467
|
-
|
|
468
|
-
def dig(*keys)
|
|
469
|
-
content.is_a?(Hash) ? content.dig(*keys) : nil
|
|
470
|
-
end
|
|
471
|
-
end
|
|
472
|
-
|
|
473
|
-
# Result wrapper for iteration execution
|
|
474
|
-
#
|
|
475
|
-
# Tracks results for each item in an iteration with
|
|
476
|
-
# aggregate success/failure counts and metrics.
|
|
477
|
-
#
|
|
478
|
-
# @api public
|
|
479
|
-
class IterationResult
|
|
480
|
-
attr_reader :step_name, :item_results, :errors
|
|
481
|
-
|
|
482
|
-
def initialize(step_name:, item_results: [], errors: {})
|
|
483
|
-
@step_name = step_name
|
|
484
|
-
@item_results = item_results
|
|
485
|
-
@errors = errors
|
|
486
|
-
end
|
|
487
|
-
|
|
488
|
-
def content
|
|
489
|
-
item_results.map do |result|
|
|
490
|
-
result.respond_to?(:content) ? result.content : result
|
|
491
|
-
end
|
|
492
|
-
end
|
|
493
|
-
|
|
494
|
-
def success?
|
|
495
|
-
errors.empty? && item_results.all? do |r|
|
|
496
|
-
!r.respond_to?(:error?) || !r.error?
|
|
497
|
-
end
|
|
498
|
-
end
|
|
499
|
-
|
|
500
|
-
def error?
|
|
501
|
-
!success?
|
|
502
|
-
end
|
|
503
|
-
|
|
504
|
-
def partial?
|
|
505
|
-
errors.any? && item_results.any? do |r|
|
|
506
|
-
!r.respond_to?(:error?) || !r.error?
|
|
507
|
-
end
|
|
508
|
-
end
|
|
509
|
-
|
|
510
|
-
def skipped?
|
|
511
|
-
false
|
|
512
|
-
end
|
|
513
|
-
|
|
514
|
-
def successful_count
|
|
515
|
-
item_results.count { |r| !r.respond_to?(:error?) || !r.error? }
|
|
516
|
-
end
|
|
517
|
-
|
|
518
|
-
def failed_count
|
|
519
|
-
errors.size + item_results.count { |r| r.respond_to?(:error?) && r.error? }
|
|
520
|
-
end
|
|
521
|
-
|
|
522
|
-
def total_count
|
|
523
|
-
item_results.size + errors.size
|
|
524
|
-
end
|
|
525
|
-
|
|
526
|
-
# Aggregate metrics across all items
|
|
527
|
-
def input_tokens
|
|
528
|
-
item_results.sum { |r| r.respond_to?(:input_tokens) ? r.input_tokens : 0 }
|
|
529
|
-
end
|
|
530
|
-
|
|
531
|
-
def output_tokens
|
|
532
|
-
item_results.sum { |r| r.respond_to?(:output_tokens) ? r.output_tokens : 0 }
|
|
533
|
-
end
|
|
534
|
-
|
|
535
|
-
def total_tokens
|
|
536
|
-
input_tokens + output_tokens
|
|
537
|
-
end
|
|
538
|
-
|
|
539
|
-
def cached_tokens
|
|
540
|
-
item_results.sum { |r| r.respond_to?(:cached_tokens) ? r.cached_tokens : 0 }
|
|
541
|
-
end
|
|
542
|
-
|
|
543
|
-
def input_cost
|
|
544
|
-
item_results.sum { |r| r.respond_to?(:input_cost) ? r.input_cost : 0.0 }
|
|
545
|
-
end
|
|
546
|
-
|
|
547
|
-
def output_cost
|
|
548
|
-
item_results.sum { |r| r.respond_to?(:output_cost) ? r.output_cost : 0.0 }
|
|
549
|
-
end
|
|
550
|
-
|
|
551
|
-
def total_cost
|
|
552
|
-
item_results.sum { |r| r.respond_to?(:total_cost) ? r.total_cost : 0.0 }
|
|
553
|
-
end
|
|
554
|
-
|
|
555
|
-
def to_h
|
|
556
|
-
{
|
|
557
|
-
step_name: step_name,
|
|
558
|
-
total_count: total_count,
|
|
559
|
-
successful_count: successful_count,
|
|
560
|
-
failed_count: failed_count,
|
|
561
|
-
success: success?,
|
|
562
|
-
items: item_results.map { |r| r.respond_to?(:to_h) ? r.to_h : r },
|
|
563
|
-
errors: errors.transform_values { |e| { class: e.class.name, message: e.message } },
|
|
564
|
-
input_tokens: input_tokens,
|
|
565
|
-
output_tokens: output_tokens,
|
|
566
|
-
total_cost: total_cost
|
|
567
|
-
}
|
|
568
|
-
end
|
|
569
|
-
|
|
570
|
-
# Access individual item results by index
|
|
571
|
-
def [](index)
|
|
572
|
-
item_results[index]
|
|
573
|
-
end
|
|
574
|
-
|
|
575
|
-
def each(&block)
|
|
576
|
-
item_results.each(&block)
|
|
577
|
-
end
|
|
578
|
-
|
|
579
|
-
def map(&block)
|
|
580
|
-
item_results.map(&block)
|
|
581
|
-
end
|
|
582
|
-
|
|
583
|
-
include Enumerable
|
|
584
|
-
|
|
585
|
-
# Empty iteration result factory
|
|
586
|
-
def self.empty(step_name)
|
|
587
|
-
new(step_name: step_name, item_results: [], errors: {})
|
|
588
|
-
end
|
|
589
|
-
end
|
|
590
|
-
end
|
|
591
|
-
end
|
|
592
|
-
end
|