Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
ai (0.8.0)
ai (0.8.1)
actionpack (>= 7.1.3)
activesupport (>= 7.1.3)
json_schemer (~> 2.4.0)
Expand Down
2 changes: 1 addition & 1 deletion lib/ai/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
# frozen_string_literal: true

module Ai
VERSION = '0.8.0'
VERSION = '0.8.1'
end
2 changes: 1 addition & 1 deletion lib/generators/ai/templates/workflow.rb.erb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ module Ai

<%= @output_struct %>

sig { params(input: Input, headers: T::Hash[String, String]).returns(Output) }
sig { override.params(input: Input, headers: T::Hash[String, String]).returns(Output) }
def self.call(input, headers: {})
response = Ai.client.run_workflow('<%= @workflow_name %>', input:, headers:)
TypeCoerce[Output].from(response)
Expand Down
8 changes: 4 additions & 4 deletions lib/generators/ai/workflow_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,18 +49,18 @@ def generate_all_workflows
validate_endpoint!

say 'Fetching workflows from Mastra...', :green
workflow_names = workflow_names
if workflow_names.empty?
names = workflow_names
if names.empty?
say 'No workflows found. Exiting.', :yellow
return
end

say "Found #{workflow_names.length} workflows: #{workflow_names.join(', ')}", :blue
say "Found #{names.length} workflows: #{names.join(', ')}", :blue
generated_count = 0
skipped_count = 0
errors = []

workflow_names.each do |workflow_name|
names.each do |workflow_name|
say "Generating workflow: #{workflow_name}"
create_workflow_file(workflow_name)
generated_count += 1
Expand Down
61 changes: 61 additions & 0 deletions spec/lib/ai/workflow_generator_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# typed: strict

require 'spec_helper'
require 'rails/generators'
require 'generators/ai/workflow_generator'

RSpec.describe Ai::Generators::WorkflowGenerator do
subject(:generator) { described_class.new([], { all: true, endpoint: endpoint }) }

let(:endpoint) { 'https://mastra.test' }

# Regression: `workflow_names = workflow_names` made the right-hand side resolve
# to the nil local being defined rather than to #workflow_names, so `--all`
# always died with "undefined method `empty?' for nil" before reaching any
# workflow. Stub the endpoint, not the generator, so the real method runs.
describe '--all' do
context 'when Mastra reports no workflows' do
before do
stub_request(:get, "#{endpoint}/api/workflows").to_return(status: 200, body: '{}')
end

it 'reports none found instead of raising on nil' do
expect { generator.send(:generate_all_workflows) }.to output(
/No workflows found/
).to_stdout
end
end

context 'when Mastra reports workflows' do
before do
stub_request(:get, "#{endpoint}/api/workflows").to_return(
status: 200,
body: { alpha: {}, beta: {} }.to_json
)
end

it 'reads the names off the response' do
expect(generator.send(:workflow_names)).to eq(%w[alpha beta])
end
end
end

# The template includes Ai::Workflow, whose `call` is abstract. Sorbet rejects an
# implementation that does not say `override.`, so generated files failed to
# typecheck in the consumer until the template declared it.
describe 'the generated template' do
let(:template) { File.read('lib/generators/ai/templates/workflow.rb.erb') }

it 'includes the workflow interface' do
expect(template).to include('include Ai::Workflow')
end

it 'declares the implementation as override' do
expect(template).to include('sig { override.params(input: Input,')
end

it 'forwards headers to the client' do
expect(template).to include('input:, headers:')
end
end
end
Loading