rcrewai-rails 0.6.1 → 0.7.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 (33) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +31 -1
  3. data/README.md +44 -0
  4. data/app/controllers/rcrewai/rails/observations_controller.rb +27 -0
  5. data/app/jobs/rcrewai/rails/crew_execution_job.rb +18 -29
  6. data/app/models/rcrewai/rails/execution.rb +6 -0
  7. data/app/models/rcrewai/rails/span.rb +57 -0
  8. data/app/models/rcrewai/rails/span_event.rb +19 -0
  9. data/app/views/rcrewai/rails/observations/_span.html.erb +51 -0
  10. data/app/views/rcrewai/rails/observations/costs.html.erb +52 -0
  11. data/app/views/rcrewai/rails/observations/show.html.erb +47 -0
  12. data/config/routes.rb +6 -0
  13. data/db/migrate/010_create_rcrewai_spans.rb +42 -0
  14. data/db/migrate/011_add_observation_rollups_to_rcrewai_executions.rb +8 -0
  15. data/docs/api.html +223 -0
  16. data/docs/capabilities.html +268 -0
  17. data/docs/examples.html +257 -0
  18. data/docs/getting-started.html +275 -0
  19. data/docs/index.html +57 -207
  20. data/lib/generators/rcrewai/rails/install/install_generator.rb +28 -16
  21. data/lib/generators/rcrewai/rails/install/templates/create_rcrewai_tables.rb +60 -0
  22. data/lib/generators/rcrewai/rails/install/templates/rcrewai.rb +17 -0
  23. data/lib/rcrewai/rails/configuration.rb +10 -1
  24. data/lib/rcrewai/rails/observation/collector.rb +301 -0
  25. data/lib/rcrewai/rails/observation/pruner.rb +48 -0
  26. data/lib/rcrewai/rails/observation/rollup.rb +60 -0
  27. data/lib/rcrewai/rails/observation/span_stack.rb +76 -0
  28. data/lib/rcrewai/rails/observation/writer.rb +89 -0
  29. data/lib/rcrewai/rails/version.rb +1 -1
  30. data/lib/rcrewai/rails.rb +13 -0
  31. data/lib/tasks/rcrewai_observation.rake +10 -0
  32. data/rcrewai-rails.gemspec +1 -1
  33. metadata +21 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 42ec86061d4e3a880fa4c985abc0f2ec55e6c7868b95c65d62f7d6de27324042
4
- data.tar.gz: f73a244bd91a867dd450f94d5fe82ab02e968f62e0a82e311a8e9e736be99e46
3
+ metadata.gz: e21a3a1057f9221cca3d280f2307b219d15a636995284c781404c4ed3d283fa6
4
+ data.tar.gz: 9b715b0cc17839eb5fb23f578905d962c4b6450d0aae2b8b1f494d549497a8b3
5
5
  SHA512:
6
- metadata.gz: 366cdfeb973451b6144514e72bcfe07e3dc3269e0d9bf093f9c73949f2f660ce6f3b8f136cf3fb1af7e27f08b5210da4c785b0f3e6ac5a9827379f764733f151
7
- data.tar.gz: b91f369cbdd7141f83e36a407742cc31af9ecff0a3563668ec50b91dd6689c82f2bd2cd40c3db6753ef693bd535e90782cf51cfa4afc2a074a2012683d20ed74
6
+ metadata.gz: 1be59f83c847e48c36b0ac61527ca9dedc61b903763be6145d6dd26354575a74d2dee3fc807a804e6f847660347c8368a28d70f33df6cb57d75d2b952b98657f
7
+ data.tar.gz: 0f37c9c304ef0264afeb363f07ca1b23c72045d9686869fe9325e336a79f30bc1930d08f97470a6235ad7d4dc8d1e5ebab4c78eb3de9d9ae1129ea42cb48dca8
data/CHANGELOG.md CHANGED
@@ -7,6 +7,35 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.7.0] - 2026-08-13
11
+
12
+ ### Added
13
+ - Observation engine: span-tree tracing of every crew execution with per-agent,
14
+ per-LLM-call and per-tool-call detail (timings, token counts, cost). Includes a
15
+ trace waterfall at `/rcrewai/executions/:id/observation`, a cost/performance
16
+ dashboard at `/rcrewai/observations/costs`, live monitoring over Turbo Streams,
17
+ and a `rcrewai:observation:prune` rake task with retention configuration
18
+ (`observation_retention_days`).
19
+
20
+ ### Fixed
21
+ - The install generator only wrote an initializer: it never copied a migration
22
+ and never mounted the engine, so following the documented install produced no
23
+ database tables and no reachable UI. It now creates a timestamped
24
+ `create_rcrewai_tables` migration, mounts the engine at `/rcrewai`, and
25
+ installs the fully documented initializer.
26
+ - The install migration template was missing the `rcrewai_tools` table and the
27
+ tasks table's `agent` reference, so a fresh install shipped a `Tool` model with
28
+ no backing table. The template now matches the schema the test suite validates.
29
+
30
+ ### Deprecated
31
+ - `Execution#log` and `ExecutionLog`, superseded by the observation engine. Both
32
+ still work and now emit a deprecation warning; scheduled for removal one minor
33
+ version after this release.
34
+
35
+ ### Requires
36
+ - `rcrewai >= 0.7.1` for agent-level tracing. On earlier 0.7.x versions traces
37
+ contain only crew-level spans.
38
+
10
39
  ## [0.6.1] - 2026-07-07
11
40
 
12
41
  ### Fixed
@@ -123,7 +152,8 @@ existing agents, tasks, and crews build unchanged.
123
152
  ### Changed
124
153
  - Rename generators from `rcrew_a_i` to `rcrewai` namespacing.
125
154
 
126
- [Unreleased]: https://github.com/gkosmo/rcrewai-rails/compare/v0.6.1...HEAD
155
+ [Unreleased]: https://github.com/gkosmo/rcrewai-rails/compare/v0.7.0...HEAD
156
+ [0.7.0]: https://github.com/gkosmo/rcrewai-rails/compare/v0.6.1...v0.7.0
127
157
  [0.6.1]: https://github.com/gkosmo/rcrewai-rails/compare/v0.6.0...v0.6.1
128
158
  [0.6.0]: https://github.com/gkosmo/rcrewai-rails/compare/v0.5.1...v0.6.0
129
159
  [0.5.1]: https://github.com/gkosmo/rcrewai-rails/compare/v0.5.0...v0.5.1
data/README.md CHANGED
@@ -16,6 +16,50 @@ Rails engine for integrating [RcrewAI](https://github.com/gkosmo/rcrewai-rails)
16
16
  - Crew: `before_kickoff`/`after_kickoff` hooks, planning, the `consensual` process, batch execution
17
17
  - Knowledge (RAG) sources and Flow persistence
18
18
 
19
+ ## Observation Engine
20
+
21
+ Every crew execution is traced as a tree of spans — crew, agent, task, LLM call, and
22
+ tool call — carrying timings, token counts, and cost.
23
+
24
+ - **Trace view** at `/rcrewai/executions/:id/observation`: a waterfall of the run, with
25
+ prompts, tool arguments, and errors on each span.
26
+ - **Cost and performance** at `/rcrewai/observations/costs`: spend and token totals
27
+ across recent executions.
28
+ - **Live monitoring**: the trace view updates over Turbo Streams while a run is in progress.
29
+
30
+ Configure it in `config/initializers/rcrewai.rb`:
31
+
32
+ ```ruby
33
+ config.observation_enabled = true
34
+ config.observation_capture_prompts = :truncated # :none | :truncated | :full
35
+ config.observation_prompt_max_bytes = 4_096
36
+ config.observation_flush_mode = :batched # :batched | :immediate
37
+ config.observation_retention_days = 30
38
+ ```
39
+
40
+ Prompt text is truncated by default: full prompts can be large and may contain personal
41
+ data. Set `:full` only when you need lossless replay.
42
+
43
+ Prune old spans with the bundled rake task:
44
+
45
+ ```bash
46
+ rake rcrewai:observation:prune # uses observation_retention_days
47
+ rake rcrewai:observation:prune DAYS=7
48
+ ```
49
+
50
+ ### Limitations
51
+
52
+ Token and cost data depend on rcrewai's **streaming** execution path. `Usage` events are
53
+ only emitted when an agent runs via `ToolRunner`, which passes a `stream:` to the LLM
54
+ client — not via `LegacyReactRunner`, which does not. `ToolRunner` is selected when the
55
+ tools have JSON schemas **and** the LLM client reports `supports_native_tools?`. OpenAI,
56
+ Anthropic, and Google all report true, so cost capture works normally with them. With a
57
+ provider or configuration that falls back to `LegacyReactRunner` (for example Ollama
58
+ without native tools), **the cost dashboard will be empty rather than showing an error**.
59
+
60
+ Agent-level tracing requires **rcrewai >= 0.7.1**, the version that threads the event
61
+ stream down to agent execution. On earlier versions traces contain only crew-level spans.
62
+
19
63
  ## Installation
20
64
 
21
65
  Add this line to your application's Gemfile:
@@ -0,0 +1,27 @@
1
+ module RcrewAI
2
+ module Rails
3
+ class ObservationsController < ApplicationController
4
+ # Hard ceiling on how deep the span partial will recurse. Real traces
5
+ # are a handful of levels deep; this only exists so a malformed tree
6
+ # cannot recurse until the request dies.
7
+ MAX_SPAN_DEPTH = 50
8
+
9
+ def show
10
+ @execution = Execution.find(params[:execution_id])
11
+ # Load the whole tree in one query and nest in memory — a recursive
12
+ # per-node query would be N+1 on deep traces.
13
+ @spans = @execution.spans.ordered.to_a
14
+ @children = @spans.group_by(&:parent_span_id)
15
+ @roots = @children[nil] || []
16
+ end
17
+
18
+ def costs
19
+ @executions = Execution.where.not(total_cost_usd: nil)
20
+ .order(created_at: :desc)
21
+ .limit(100)
22
+ @total_cost = @executions.sum(&:total_cost_usd)
23
+ @total_tokens = @executions.sum { |e| e.total_tokens.to_i }
24
+ end
25
+ end
26
+ end
27
+ end
@@ -11,6 +11,7 @@ module RcrewAI
11
11
  inputs: inputs,
12
12
  batch_id: batch_id
13
13
  )
14
+ collector = nil
14
15
 
15
16
  begin
16
17
  execution.start!
@@ -18,7 +19,16 @@ module RcrewAI
18
19
 
19
20
  rcrew = crew.to_rcrew
20
21
 
21
- result = rcrew.execute(stream: stream_sink_for(execution))
22
+ collector = collector_for(execution)
23
+ collector&.start_crew_span(crew_name: crew.name)
24
+
25
+ result = rcrew.execute(stream: collector)
26
+
27
+ # Close agent spans as successful before finish!, which treats
28
+ # anything still open as an aborted run.
29
+ collector&.finish_open_agent_spans(status: "ok")
30
+ collector&.finish_crew_span(status: "ok")
31
+ collector&.finish!
22
32
 
23
33
  execution.complete!(result)
24
34
  execution.log("info", "Crew execution completed", { result: result })
@@ -27,6 +37,7 @@ module RcrewAI
27
37
 
28
38
  result
29
39
  rescue => e
40
+ collector&.finish!
30
41
  execution.fail!(e)
31
42
  execution.log("error", "Crew execution failed", {
32
43
  error: e.message,
@@ -39,34 +50,12 @@ module RcrewAI
39
50
 
40
51
  private
41
52
 
42
- # Build a stream sink that translates rcrewai events into ExecutionLog rows.
43
- # Note: the gem currently builds the sink but does not yet thread it down
44
- # to per-agent execution, so this is wired up for forward-compatibility.
45
- def stream_sink_for(execution)
46
- lambda do |event|
47
- case event
48
- when RCrewAI::Events::IterationStart
49
- execution.log("debug", "Iteration #{event.iteration_index} start", { agent: event.agent })
50
- when RCrewAI::Events::IterationEnd
51
- execution.log("debug", "Iteration end", { agent: event.agent, finish_reason: event.finish_reason })
52
- when RCrewAI::Events::ToolCallStart
53
- execution.log("info", "Tool call: #{event.tool}", { args: event.args, agent: event.agent })
54
- when RCrewAI::Events::ToolCallResult
55
- execution.log("info", "Tool result: #{event.tool}", { duration_ms: event.duration_ms, agent: event.agent })
56
- when RCrewAI::Events::ToolCallError
57
- execution.log("error", "Tool error: #{event.tool}", { error: event.error, agent: event.agent })
58
- when RCrewAI::Events::Usage
59
- execution.log("debug", "Usage", {
60
- prompt_tokens: event.prompt_tokens,
61
- completion_tokens: event.completion_tokens,
62
- total_tokens: event.total_tokens,
63
- cost_usd: event.cost_usd,
64
- agent: event.agent
65
- })
66
- when RCrewAI::Events::Error
67
- execution.log("error", "Crew error", { error: event.error, agent: event.agent })
68
- end
69
- end
53
+ # Translates rcrewai events into the span tree. Returns nil when
54
+ # observation is disabled so no sink is attached at all.
55
+ def collector_for(execution)
56
+ return nil unless RcrewAI::Rails.config.observation_enabled
57
+
58
+ RcrewAI::Rails::Observation::Collector.new(execution: execution)
70
59
  end
71
60
 
72
61
  def notify_completion(crew, execution, result)
@@ -5,6 +5,7 @@ module RcrewAI
5
5
 
6
6
  belongs_to :crew
7
7
  has_many :execution_logs, dependent: :destroy
8
+ has_many :spans, dependent: :destroy
8
9
 
9
10
  validates :status, inclusion: { in: %w[pending running completed failed cancelled] }
10
11
 
@@ -74,7 +75,12 @@ module RcrewAI
74
75
  %w[completed failed cancelled].include?(status)
75
76
  end
76
77
 
78
+ # Deprecated: superseded by the observation engine's span tree.
79
+ # Scheduled for removal one minor version after the engine ships.
77
80
  def log(level, message, details = {})
81
+ RcrewAI::Rails.deprecator_warn(
82
+ "Execution#log and ExecutionLog are deprecated; use the observation engine (Execution#spans)."
83
+ )
78
84
  execution_logs.create!(
79
85
  level: level,
80
86
  message: message,
@@ -0,0 +1,57 @@
1
+ module RcrewAI
2
+ module Rails
3
+ class Span < ApplicationRecord
4
+ self.table_name = "rcrewai_spans"
5
+
6
+ KINDS = %w[crew agent task llm_call tool_call].freeze
7
+ STATUSES = %w[running ok error].freeze
8
+
9
+ belongs_to :execution
10
+ belongs_to :parent, class_name: "RcrewAI::Rails::Span",
11
+ foreign_key: :parent_span_id, optional: true
12
+ has_many :children, class_name: "RcrewAI::Rails::Span",
13
+ foreign_key: :parent_span_id, dependent: :destroy
14
+ has_many :span_events, dependent: :destroy
15
+
16
+ validates :kind, inclusion: { in: KINDS }
17
+ validates :status, inclusion: { in: STATUSES }
18
+ validates :name, :trace_id, :started_at, :sequence, presence: true
19
+
20
+ scope :roots, -> { where(parent_span_id: nil).order(:sequence) }
21
+ scope :ordered, -> { order(:sequence) }
22
+ scope :errored, -> { where(status: "error") }
23
+ scope :running, -> { where(status: "running") }
24
+ scope :llm_calls, -> { where(kind: "llm_call") }
25
+ scope :tool_calls, -> { where(kind: "tool_call") }
26
+
27
+ def attributes_hash
28
+ raw = self[:attributes_json]
29
+ return {} if raw.blank?
30
+
31
+ JSON.parse(raw)
32
+ rescue JSON::ParserError
33
+ {}
34
+ end
35
+
36
+ def attributes_hash=(hash)
37
+ self[:attributes_json] = hash.nil? ? nil : JSON.generate(hash)
38
+ end
39
+
40
+ def finish!(status: "ok", ended_at: Time.current)
41
+ update!(
42
+ status: status,
43
+ ended_at: ended_at,
44
+ duration_ms: ((ended_at - started_at) * 1000).round
45
+ )
46
+ end
47
+
48
+ def running?
49
+ status == "running"
50
+ end
51
+
52
+ def errored?
53
+ status == "error"
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,19 @@
1
+ module RcrewAI
2
+ module Rails
3
+ class SpanEvent < ApplicationRecord
4
+ self.table_name = "rcrewai_span_events"
5
+
6
+ LEVELS = %w[debug info warn error].freeze
7
+
8
+ belongs_to :span
9
+
10
+ validates :level, inclusion: { in: LEVELS }
11
+ validates :name, presence: true
12
+
13
+ serialize :details, coder: JSON
14
+
15
+ scope :errors, -> { where(level: "error") }
16
+ scope :recent, -> { order(timestamp: :desc) }
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,51 @@
1
+ <%#
2
+ Renders one span and, recursively, its children.
3
+
4
+ `children` is the parent_span_id => [spans] map built once by the
5
+ controller. Children are read from that map, never via `span.children`,
6
+ which would issue a query per node and turn a deep trace into an N+1.
7
+
8
+ A malformed tree (a parent/child cycle, e.g. a bad backfill) would
9
+ otherwise recurse until the request dies, so recursion is bounded by
10
+ MAX_SPAN_DEPTH and by `seen`, which drops any span already on the path.
11
+ %>
12
+ <% depth = local_assigns.fetch(:depth, 0) %>
13
+ <% children = local_assigns.fetch(:children, {}) %>
14
+ <% seen = local_assigns.fetch(:seen, []) %>
15
+ <% max_depth = RcrewAI::Rails::ObservationsController::MAX_SPAN_DEPTH %>
16
+
17
+ <div class="span-row span-<%= span.status %> span-kind-<%= span.kind %>" id="span-<%= span.id %>">
18
+ <div class="span-line" style="padding-left: <%= depth * 20 %>px">
19
+ <span class="span-kind"><%= span.kind %></span>
20
+ <span class="span-name"><%= span.name %></span>
21
+ <span class="span-status status-<%= span.status %>"><%= span.status %></span>
22
+ <% if span.duration_ms %>
23
+ <span class="span-duration"><%= span.duration_ms %>ms</span>
24
+ <% end %>
25
+ <% if span.total_tokens.to_i.positive? %>
26
+ <span class="span-tokens"><%= span.total_tokens %> tokens</span>
27
+ <% end %>
28
+ <% if span.cost_usd.present? %>
29
+ <span class="span-cost">$<%= span.cost_usd %></span>
30
+ <% end %>
31
+ </div>
32
+
33
+ <% attrs = span.attributes_hash %>
34
+ <% if attrs.present? %>
35
+ <details class="span-attributes" style="margin-left: <%= (depth * 20) + 20 %>px">
36
+ <summary>Details</summary>
37
+ <pre class="code-block"><%= JSON.pretty_generate(attrs) %></pre>
38
+ </details>
39
+ <% end %>
40
+ </div>
41
+
42
+ <% if depth < max_depth %>
43
+ <% (children[span.id] || []).each do |child| %>
44
+ <% next if seen.include?(child.id) %>
45
+ <%= render "rcrewai/rails/observations/span",
46
+ span: child,
47
+ children: children,
48
+ depth: depth + 1,
49
+ seen: seen + [span.id] %>
50
+ <% end %>
51
+ <% end %>
@@ -0,0 +1,52 @@
1
+ <div class="page-header">
2
+ <h1>Cost &amp; Performance</h1>
3
+ </div>
4
+
5
+ <div class="metadata-grid">
6
+ <div class="meta-item">
7
+ <strong>Total Cost:</strong> $<%= @total_cost %>
8
+ </div>
9
+ <div class="meta-item">
10
+ <strong>Total Tokens:</strong> <%= @total_tokens %>
11
+ </div>
12
+ <div class="meta-item">
13
+ <strong>Executions:</strong> <%= @executions.size %>
14
+ </div>
15
+ </div>
16
+
17
+ <div class="section">
18
+ <h2>Recent Executions</h2>
19
+
20
+ <% if @executions.empty? %>
21
+ <p class="empty-state">No executions with recorded cost yet.</p>
22
+ <% else %>
23
+ <table class="table">
24
+ <thead>
25
+ <tr>
26
+ <th>Execution</th>
27
+ <th>Crew</th>
28
+ <th>Status</th>
29
+ <th>Spans</th>
30
+ <th>Errors</th>
31
+ <th>Tokens</th>
32
+ <th>Cost</th>
33
+ <th></th>
34
+ </tr>
35
+ </thead>
36
+ <tbody>
37
+ <% @executions.each do |execution| %>
38
+ <tr>
39
+ <td><%= link_to "##{execution.id}", execution_path(execution) %></td>
40
+ <td><%= execution.crew&.name %></td>
41
+ <td><span class="status-<%= execution.status %>"><%= execution.status.humanize %></span></td>
42
+ <td><%= execution.span_count.to_i %></td>
43
+ <td><%= execution.error_count.to_i %></td>
44
+ <td><%= execution.total_tokens.to_i %></td>
45
+ <td>$<%= execution.total_cost_usd %></td>
46
+ <td><%= link_to "Trace", execution_observation_path(execution), class: "btn btn-sm" %></td>
47
+ </tr>
48
+ <% end %>
49
+ </tbody>
50
+ </table>
51
+ <% end %>
52
+ </div>
@@ -0,0 +1,47 @@
1
+ <div class="page-header">
2
+ <h1>Trace for Execution #<%= @execution.id %></h1>
3
+ <div class="actions">
4
+ <%= link_to "Back to Execution", execution_path(@execution), class: "btn" %>
5
+ <%= link_to "Costs", observation_costs_path, class: "btn" %>
6
+ </div>
7
+ </div>
8
+
9
+ <div class="execution-details">
10
+ <div class="status-banner status-<%= @execution.status %>">
11
+ <strong>Status:</strong> <%= @execution.status.humanize %>
12
+ </div>
13
+
14
+ <div class="metadata-grid">
15
+ <div class="meta-item">
16
+ <strong>Spans:</strong> <%= @execution.span_count.to_i %>
17
+ </div>
18
+ <div class="meta-item">
19
+ <strong>Errors:</strong> <%= @execution.error_count.to_i %>
20
+ </div>
21
+ <div class="meta-item">
22
+ <strong>Tokens:</strong> <%= @execution.total_tokens.to_i %>
23
+ </div>
24
+ <div class="meta-item">
25
+ <strong>Cost:</strong> <%= @execution.total_cost_usd.present? ? "$#{@execution.total_cost_usd}" : "N/A" %>
26
+ </div>
27
+ </div>
28
+ </div>
29
+
30
+ <div class="section">
31
+ <h2>Waterfall</h2>
32
+
33
+ <div class="span-tree" id="execution-<%= @execution.id %>-spans">
34
+ <% if @roots.empty? %>
35
+ <p class="empty-state">No spans recorded for this execution.</p>
36
+ <% else %>
37
+ <% @roots.each do |span| %>
38
+ <%= render "rcrewai/rails/observations/span",
39
+ span: span, children: @children, depth: 0, seen: [] %>
40
+ <% end %>
41
+ <% end %>
42
+ </div>
43
+ </div>
44
+
45
+ <% if defined?(turbo_stream_from) && @execution.running? %>
46
+ <%= turbo_stream_from "rcrewai_execution_#{@execution.id}" %>
47
+ <% end %>
data/config/routes.rb CHANGED
@@ -16,6 +16,12 @@ RcrewAI::Rails::Engine.routes.draw do
16
16
  end
17
17
  end
18
18
 
19
+ resources :executions, only: [] do
20
+ resource :observation, only: [:show], controller: "observations"
21
+ end
22
+
23
+ get "observations/costs", to: "observations#costs", as: :observation_costs
24
+
19
25
  resources :agents do
20
26
  resources :tools
21
27
  end
@@ -0,0 +1,42 @@
1
+ class CreateRcrewaiSpans < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :rcrewai_spans do |t|
4
+ t.references :execution, null: false, foreign_key: { to_table: :rcrewai_executions }
5
+ t.bigint :parent_span_id
6
+ t.string :trace_id, null: false
7
+ t.string :kind, null: false
8
+ t.string :name, null: false
9
+ t.string :status, null: false, default: "running"
10
+ t.datetime :started_at, null: false
11
+ t.datetime :ended_at
12
+ t.integer :duration_ms
13
+ t.integer :prompt_tokens
14
+ t.integer :completion_tokens
15
+ t.integer :total_tokens
16
+ t.decimal :cost_usd, precision: 12, scale: 6
17
+ t.text :attributes_json
18
+ t.integer :sequence, null: false
19
+
20
+ t.timestamps
21
+ end
22
+
23
+ add_index :rcrewai_spans, :parent_span_id
24
+ add_index :rcrewai_spans, :trace_id
25
+ add_index :rcrewai_spans, :kind
26
+ add_index :rcrewai_spans, :status
27
+ add_index :rcrewai_spans, %i[execution_id sequence]
28
+
29
+ create_table :rcrewai_span_events do |t|
30
+ t.references :span, null: false, foreign_key: { to_table: :rcrewai_spans }
31
+ t.string :level, null: false, default: "info"
32
+ t.string :name, null: false
33
+ t.text :details
34
+ t.datetime :timestamp, null: false
35
+
36
+ t.timestamps
37
+ end
38
+
39
+ add_index :rcrewai_span_events, :level
40
+ add_index :rcrewai_span_events, :timestamp
41
+ end
42
+ end
@@ -0,0 +1,8 @@
1
+ class AddObservationRollupsToRcrewaiExecutions < ActiveRecord::Migration[7.0]
2
+ def change
3
+ add_column :rcrewai_executions, :total_cost_usd, :decimal, precision: 12, scale: 6
4
+ add_column :rcrewai_executions, :total_tokens, :integer
5
+ add_column :rcrewai_executions, :span_count, :integer, default: 0, null: false
6
+ add_column :rcrewai_executions, :error_count, :integer, default: 0, null: false
7
+ end
8
+ end