generative_ui 0.0.1

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.
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+ require 'active_support/concern'
5
+ require 'active_support/core_ext'
6
+ require 'ruby_llm/schema'
7
+ require 'json'
8
+
9
+ require_relative 'generative_ui/version'
10
+ require_relative 'generative_ui/structural_ref'
11
+ require_relative 'generative_ui/attributes'
12
+ require_relative 'generative_ui/component_definition'
13
+ require_relative 'generative_ui/component'
14
+ require_relative 'generative_ui/component_set'
15
+ require_relative 'generative_ui/component_validator'
16
+ require_relative 'generative_ui/component_tree_validator'
17
+ require_relative 'generative_ui/invalid_component_tree_error'
18
+ require_relative 'generative_ui/tool'
19
+ require_relative 'generative_ui/conventions'
20
+ require_relative 'generative_ui/catalog'
21
+ require_relative 'generative_ui/renderer'
22
+ require_relative 'generative_ui/renderers/partial'
23
+ require_relative 'generative_ui/renderers/view_component'
24
+ require_relative 'generative_ui/renderers/json'
25
+ require_relative 'generative_ui/engine' if defined?(::Rails::Engine)
26
+
27
+ module GenerativeUI
28
+ class Configuration
29
+ attr_accessor :default_renderer
30
+ attr_reader :renderers
31
+
32
+ def initialize
33
+ @renderers = {}
34
+ @catalogs = {}
35
+ @default_renderer = :partial
36
+
37
+ register_renderer(:partial) do |view_context|
38
+ GenerativeUI::Renderers::Partial.new(view_context: view_context)
39
+ end
40
+
41
+ register_renderer(:view_component) do |view_context|
42
+ GenerativeUI::Renderers::ViewComponent.new(view_context: view_context)
43
+ end
44
+
45
+ register_renderer(:json) do |_view_context|
46
+ GenerativeUI::Renderers::Json.new
47
+ end
48
+ end
49
+
50
+ def register_renderer(name, &factory)
51
+ renderers[name.to_sym] = factory
52
+ end
53
+
54
+ def catalog(name, catalog_class = nil)
55
+ if catalog_class.nil?
56
+ @catalogs[name.to_sym]
57
+ else
58
+ @catalogs[name.to_sym] = normalize_catalog_value(catalog_class)
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ def normalize_catalog_value(value)
65
+ value.is_a?(Class) && value.name ? value.name : value
66
+ end
67
+ end
68
+
69
+ class << self
70
+ def configure
71
+ yield configuration
72
+ end
73
+
74
+ def configuration
75
+ @configuration ||= Configuration.new
76
+ end
77
+
78
+ def reset_configuration!
79
+ @configuration = Configuration.new
80
+ end
81
+
82
+ def renderer_for(view_context, renderer: nil, catalog: nil)
83
+ name = (renderer || configuration.default_renderer).to_sym
84
+ factory = configuration.renderers[name]
85
+ raise ArgumentError, "Unknown renderer: #{name}" unless factory
86
+
87
+ factory.call(view_context).tap do |renderer|
88
+ renderer.catalog = catalog if catalog && renderer.respond_to?(:catalog=)
89
+ end
90
+ end
91
+
92
+ def render(arguments, renderer:, catalog: :default, view_context: nil)
93
+ raise ArgumentError, 'arguments must be a Hash' unless arguments.is_a?(Hash)
94
+
95
+ unknown = arguments.keys.map(&:to_s) - %w[components]
96
+ raise InvalidComponentTreeError, { '_arguments' => ["unknown arguments: #{unknown.join(', ')}"] } if unknown.any?
97
+
98
+ components = arguments['components'] || arguments[:components]
99
+ raise InvalidComponentTreeError, { '_arguments' => ['components must be an array'] } unless components.is_a?(Array)
100
+
101
+ catalog = Catalog.coerce(catalog)
102
+ raise ArgumentError, 'generative UI catalog is empty' if catalog.empty?
103
+
104
+ set = ComponentSet.from_args(components)
105
+ validation = ComponentTreeValidator.call(set, catalog:)
106
+ raise InvalidComponentTreeError, validation.errors unless validation.valid?
107
+
108
+ resolve_renderer(renderer, view_context:, catalog:).call(set)
109
+ end
110
+
111
+ private
112
+
113
+ def resolve_renderer(renderer, view_context:, catalog:)
114
+ if renderer.is_a?(Symbol)
115
+ renderer_for(view_context, renderer:, catalog:)
116
+ else
117
+ renderer.tap { |instance| instance.catalog = catalog if instance.respond_to?(:catalog=) }
118
+ end
119
+ end
120
+ end
121
+ end
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: generative_ui
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Andrey Samsonov
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: activesupport
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '7.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '7.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: ruby_llm
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '1.15'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.15'
40
+ - !ruby/object:Gem::Dependency
41
+ name: json_schemer
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '2.5'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '2.5'
54
+ description: Define a safe component catalog, expose it as a RubyLLM tool schema,
55
+ validate model-generated component trees, and render them with Rails partials, ViewComponent,
56
+ or JSON.
57
+ email:
58
+ - me@samsonov.io
59
+ executables: []
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - LICENSE.txt
64
+ - README.md
65
+ - app/helpers/generative_ui/view_helper.rb
66
+ - app/views/messages/tool_calls/_generate_ui.html.erb
67
+ - app/views/messages/tool_results/_generate_ui.html.erb
68
+ - generative_ui.gemspec
69
+ - lib/generative_ui.rb
70
+ - lib/generative_ui/attributes.rb
71
+ - lib/generative_ui/catalog.rb
72
+ - lib/generative_ui/component.rb
73
+ - lib/generative_ui/component_definition.rb
74
+ - lib/generative_ui/component_set.rb
75
+ - lib/generative_ui/component_tree_validator.rb
76
+ - lib/generative_ui/component_validator.rb
77
+ - lib/generative_ui/conventions.rb
78
+ - lib/generative_ui/engine.rb
79
+ - lib/generative_ui/invalid_component_tree_error.rb
80
+ - lib/generative_ui/renderer.rb
81
+ - lib/generative_ui/renderers/json.rb
82
+ - lib/generative_ui/renderers/partial.rb
83
+ - lib/generative_ui/renderers/view_component.rb
84
+ - lib/generative_ui/structural_ref.rb
85
+ - lib/generative_ui/tool.rb
86
+ - lib/generative_ui/version.rb
87
+ homepage: https://github.com/kryzhovnik/generative_ui
88
+ licenses:
89
+ - MIT
90
+ metadata:
91
+ source_code_uri: https://github.com/kryzhovnik/generative_ui
92
+ rdoc_options: []
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: 3.2.0
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ requirements: []
106
+ rubygems_version: 3.6.9
107
+ specification_version: 4
108
+ summary: Catalog-driven generative UI for RubyLLM and Rails
109
+ test_files: []