prompt_builder 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 (51) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +24 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.md +763 -0
  5. data/VERSION +1 -0
  6. data/lib/prompt_builder/content/base.rb +44 -0
  7. data/lib/prompt_builder/content/input_file.rb +63 -0
  8. data/lib/prompt_builder/content/input_image.rb +64 -0
  9. data/lib/prompt_builder/content/input_text.rb +42 -0
  10. data/lib/prompt_builder/content/input_video.rb +43 -0
  11. data/lib/prompt_builder/content/output_text.rb +59 -0
  12. data/lib/prompt_builder/content/reasoning_text.rb +42 -0
  13. data/lib/prompt_builder/content/refusal_content.rb +42 -0
  14. data/lib/prompt_builder/content/summary_text.rb +42 -0
  15. data/lib/prompt_builder/content/text.rb +42 -0
  16. data/lib/prompt_builder/content.rb +28 -0
  17. data/lib/prompt_builder/errors.rb +18 -0
  18. data/lib/prompt_builder/items/base.rb +41 -0
  19. data/lib/prompt_builder/items/compaction.rb +60 -0
  20. data/lib/prompt_builder/items/function_call.rb +97 -0
  21. data/lib/prompt_builder/items/function_call_output.rb +110 -0
  22. data/lib/prompt_builder/items/item_reference.rb +42 -0
  23. data/lib/prompt_builder/items/message.rb +113 -0
  24. data/lib/prompt_builder/items/reasoning.rb +75 -0
  25. data/lib/prompt_builder/items.rb +13 -0
  26. data/lib/prompt_builder/response.rb +257 -0
  27. data/lib/prompt_builder/serializers/base.rb +37 -0
  28. data/lib/prompt_builder/serializers/chat_completion/request.rb +389 -0
  29. data/lib/prompt_builder/serializers/chat_completion/response.rb +139 -0
  30. data/lib/prompt_builder/serializers/chat_completion.rb +30 -0
  31. data/lib/prompt_builder/serializers/converse/request.rb +623 -0
  32. data/lib/prompt_builder/serializers/converse/response.rb +140 -0
  33. data/lib/prompt_builder/serializers/converse.rb +30 -0
  34. data/lib/prompt_builder/serializers/gemini/request.rb +562 -0
  35. data/lib/prompt_builder/serializers/gemini/response.rb +233 -0
  36. data/lib/prompt_builder/serializers/gemini.rb +30 -0
  37. data/lib/prompt_builder/serializers/messages/request.rb +634 -0
  38. data/lib/prompt_builder/serializers/messages/response.rb +157 -0
  39. data/lib/prompt_builder/serializers/messages.rb +30 -0
  40. data/lib/prompt_builder/serializers/open_responses/request.rb +229 -0
  41. data/lib/prompt_builder/serializers/open_responses/response.rb +18 -0
  42. data/lib/prompt_builder/serializers/open_responses.rb +30 -0
  43. data/lib/prompt_builder/serializers.rb +35 -0
  44. data/lib/prompt_builder/session.rb +383 -0
  45. data/lib/prompt_builder/tool_registry.rb +75 -0
  46. data/lib/prompt_builder/tools/definition.rb +66 -0
  47. data/lib/prompt_builder/tools.rb +7 -0
  48. data/lib/prompt_builder/usage.rb +100 -0
  49. data/lib/prompt_builder.rb +86 -0
  50. data/prompt_builder.gemspec +41 -0
  51. metadata +107 -0
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module PromptBuilder
6
+ module Items
7
+ # Represents a function call item in a conversation. This is output
8
+ # by the model when it wants to invoke a tool.
9
+ class FunctionCall < Base
10
+ # @return [String] the function name
11
+ attr_reader :name
12
+
13
+ # @return [String] the unique call identifier
14
+ attr_reader :call_id
15
+
16
+ # @return [String] the JSON-encoded arguments string (always a Hash-shaped JSON object)
17
+ attr_reader :arguments
18
+
19
+ # @return [String, nil] the item identifier
20
+ attr_reader :id
21
+
22
+ # @return [String, nil] the call status
23
+ attr_reader :status
24
+
25
+ # @return [Hash, nil] provider-specific extra data
26
+ attr_reader :extra
27
+
28
+ # Create a new FunctionCall item.
29
+ #
30
+ # @param name [String] the function name
31
+ # @param call_id [String] the unique call identifier
32
+ # @param arguments [String, Hash, nil] the function arguments; a String is
33
+ # stored as-is (must be valid JSON), a Hash is JSON-encoded, and nil
34
+ # defaults to an empty JSON object (+"{}"+ )
35
+ # @param id [String, nil] the item identifier
36
+ # @param status [String, nil] the call status
37
+ # @param extra [Hash] provider-specific extra keyword arguments
38
+ # @raise [ArgumentError] if arguments is not a String, Hash, or nil
39
+ def initialize(name:, call_id:, arguments:, id: nil, status: nil, **extra)
40
+ @name = name&.to_s
41
+ @call_id = call_id&.to_s
42
+ @arguments = case arguments
43
+ when nil then "{}"
44
+ when String then arguments
45
+ when Hash then JSON.generate(arguments)
46
+ else raise ArgumentError, "FunctionCall arguments must be a String, Hash, or nil; got #{arguments.class}"
47
+ end
48
+ @id = id&.to_s
49
+ @status = status&.to_s
50
+ @extra = extra.transform_keys(&:to_s)
51
+ end
52
+
53
+ class << self
54
+ # Deserialize a FunctionCall from a Hash.
55
+ #
56
+ # @param hash [Hash] a Hash with string keys
57
+ # @return [FunctionCall]
58
+ def from_h(hash)
59
+ new(
60
+ name: hash["name"],
61
+ call_id: hash["call_id"],
62
+ arguments: hash["arguments"],
63
+ id: hash["id"],
64
+ status: hash["status"],
65
+ **hash.except("type", "id", "name", "call_id", "arguments", "status").transform_keys(&:to_sym)
66
+ )
67
+ end
68
+ end
69
+
70
+ # Parse the JSON arguments string into a Hash.
71
+ #
72
+ # @raise [PromptBuilder::InvalidItemError] if the arguments string is not valid JSON
73
+ # @return [Hash] the parsed arguments
74
+ def parsed_arguments
75
+ JSON.parse(@arguments)
76
+ rescue JSON::ParserError => e
77
+ raise PromptBuilder::InvalidItemError, "Invalid JSON in function call arguments for '#{@name}': #{e.message}"
78
+ end
79
+
80
+ # Serialize to a Hash with string keys. Nil values are omitted.
81
+ #
82
+ # @return [Hash]
83
+ def to_h
84
+ h = {
85
+ "type" => "function_call",
86
+ "name" => @name,
87
+ "call_id" => @call_id,
88
+ "arguments" => @arguments
89
+ }
90
+ h["id"] = @id if @id
91
+ h["status"] = @status if @status
92
+ h = PromptBuilder.jsonify(@extra).merge(h) unless @extra.empty?
93
+ h
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PromptBuilder
4
+ module Items
5
+ # Represents the output of a function call. This is added to the
6
+ # conversation after a tool has been invoked.
7
+ class FunctionCallOutput < Base
8
+ # @return [String, nil] the function call output identifier
9
+ attr_reader :id
10
+
11
+ # @return [String] the call identifier this output corresponds to
12
+ attr_reader :call_id
13
+
14
+ # @return [String, nil] the function call output status
15
+ attr_reader :status
16
+
17
+ # @return [String, Array<Content::Base>, nil] the output from the function; either a plain
18
+ # string, an array of content objects, or nil (serialized as +""+)
19
+ attr_reader :output
20
+
21
+ # @return [Hash, nil] provider-specific extra data
22
+ attr_reader :extra
23
+
24
+ # Create a new FunctionCallOutput item.
25
+ #
26
+ # @param id [String, nil] the function call output identifier
27
+ # @param call_id [String] the call identifier
28
+ # @param status [String, nil] the function call output status
29
+ # @param output [String, Array<Content::Base, Hash>, nil] the function output;
30
+ # Hash elements in an array are normalized into +Content::Base+ objects
31
+ # @param extra [Hash] provider-specific extra keyword arguments
32
+ def initialize(call_id:, output:, id: nil, status: nil, **extra)
33
+ @id = id&.to_s
34
+ @call_id = call_id&.to_s
35
+ @status = status&.to_s
36
+ @output = normalize_output(output)
37
+ @extra = extra.transform_keys(&:to_s)
38
+ end
39
+
40
+ class << self
41
+ # Deserialize a FunctionCallOutput from a Hash.
42
+ #
43
+ # @param hash [Hash] a Hash with string keys
44
+ # @return [FunctionCallOutput]
45
+ def from_h(hash)
46
+ output = hash["output"]
47
+ output = output.map { |c| Content::Base.from_h(c) } if output.is_a?(Array)
48
+ new(
49
+ id: hash["id"],
50
+ call_id: hash["call_id"],
51
+ status: hash["status"],
52
+ output: output,
53
+ **hash.except("type", "id", "call_id", "status", "output").transform_keys(&:to_sym)
54
+ )
55
+ end
56
+ end
57
+
58
+ # Serialize to a Hash with string keys. A nil output is emitted as an
59
+ # empty string so the on-the-wire shape matches the Open Responses API.
60
+ #
61
+ # @return [Hash]
62
+ def to_h
63
+ hash = {
64
+ "type" => "function_call_output",
65
+ "call_id" => @call_id,
66
+ "output" => serialize_output
67
+ }
68
+ hash["id"] = @id if @id
69
+ hash["status"] = @status if @status
70
+ hash = PromptBuilder.jsonify(@extra).merge(hash) unless @extra.empty?
71
+ hash
72
+ end
73
+
74
+ private
75
+
76
+ def serialize_output
77
+ case @output
78
+ when nil then ""
79
+ when Array then @output.map(&:to_h)
80
+ else @output
81
+ end
82
+ end
83
+
84
+ def normalize_output(output)
85
+ case output
86
+ when String, NilClass
87
+ output
88
+ when Array
89
+ output.map do |c|
90
+ case c
91
+ when Hash
92
+ hash = c.transform_keys(&:to_s)
93
+ unless hash["type"]
94
+ raise InvalidItemError,
95
+ "Output content hash is missing required \"type\" key: #{hash.inspect}"
96
+ end
97
+ Content::Base.from_h(hash)
98
+ when Content::Base
99
+ c
100
+ else
101
+ raise InvalidItemError, "Unsupported output element: #{c.class}"
102
+ end
103
+ end
104
+ else
105
+ raise InvalidItemError, "Unsupported output type: #{output.class}"
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PromptBuilder
4
+ module Items
5
+ # Represents a reference to an existing conversation item by ID.
6
+ class ItemReference < Base
7
+ # @return [String] the referenced item identifier
8
+ attr_reader :id
9
+
10
+ # @return [Hash, nil] provider-specific extra data
11
+ attr_reader :extra
12
+
13
+ # Create a new ItemReference item.
14
+ #
15
+ # @param id [String] the referenced item identifier
16
+ # @param extra [Hash] provider-specific extra keyword arguments
17
+ def initialize(id:, **extra)
18
+ @id = id&.to_s
19
+ @extra = extra.transform_keys(&:to_s)
20
+ end
21
+
22
+ class << self
23
+ # Deserialize an ItemReference from a Hash.
24
+ #
25
+ # @param hash [Hash] a Hash with string keys
26
+ # @return [ItemReference]
27
+ def from_h(hash)
28
+ new(id: hash["id"], **hash.except("type", "id").transform_keys(&:to_sym))
29
+ end
30
+ end
31
+
32
+ # Serialize to a Hash with string keys.
33
+ #
34
+ # @return [Hash]
35
+ def to_h
36
+ h = {"type" => "item_reference", "id" => @id}
37
+ h = PromptBuilder.jsonify(@extra).merge(h) unless @extra.empty?
38
+ h
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PromptBuilder
4
+ module Items
5
+ # Represents a message item in a conversation. Messages have a role
6
+ # and an array of content objects.
7
+ class Message < Base
8
+ # @return [String, nil] the message identifier
9
+ attr_reader :id
10
+
11
+ # @return [String] the message role (e.g. "user", "assistant", "system", "developer")
12
+ attr_reader :role
13
+
14
+ # @return [String, nil] the message status
15
+ attr_reader :status
16
+
17
+ # @return [String, nil] the message phase
18
+ attr_reader :phase
19
+
20
+ # @return [Array<Content::Base>] the content objects
21
+ attr_reader :content
22
+
23
+ # @return [Hash, nil] provider-specific extra data
24
+ attr_reader :extra
25
+
26
+ # Create a new Message item.
27
+ #
28
+ # @param id [String, nil] the message identifier
29
+ # @param role [String] the message role
30
+ # @param status [String, nil] the message status
31
+ # @param phase [String, nil] the message phase
32
+ # @param content [Array<Content::Base>] the content objects
33
+ # @param extra [Hash] provider-specific extra keyword arguments
34
+ def initialize(role:, content:, id: nil, status: nil, phase: nil, **extra)
35
+ @id = id&.to_s
36
+ @role = role&.to_s
37
+ @status = status&.to_s
38
+ @phase = phase&.to_s
39
+ @content = normalize_content(content)
40
+ @extra = extra.transform_keys(&:to_s)
41
+ end
42
+
43
+ class << self
44
+ # Deserialize a Message from a Hash.
45
+ #
46
+ # @param hash [Hash] a Hash with string keys
47
+ # @return [Message]
48
+ def from_h(hash)
49
+ content = (hash["content"] || []).map { |c| Content::Base.from_h(c) }
50
+ new(
51
+ id: hash["id"],
52
+ role: hash["role"],
53
+ status: hash["status"],
54
+ phase: hash["phase"],
55
+ content: content,
56
+ **hash.except("type", "id", "role", "status", "phase", "content").transform_keys(&:to_sym)
57
+ )
58
+ end
59
+ end
60
+
61
+ # Serialize to a Hash with string keys.
62
+ #
63
+ # @return [Hash]
64
+ def to_h
65
+ hash = {
66
+ "type" => "message",
67
+ "role" => @role,
68
+ "content" => @content.map(&:to_h)
69
+ }
70
+ hash["id"] = @id if @id
71
+ hash["status"] = @status if @status
72
+ hash["phase"] = @phase if @phase
73
+ hash = PromptBuilder.jsonify(@extra).merge(hash) unless @extra.empty?
74
+ hash
75
+ end
76
+
77
+ private
78
+
79
+ def normalize_content(content)
80
+ case content
81
+ when String
82
+ [Content::InputText.new(text: content)]
83
+ when Content::Base
84
+ [content]
85
+ when Hash
86
+ [normalize_hash_content(content)]
87
+ when Array
88
+ content.map do |c|
89
+ case c
90
+ when Hash
91
+ normalize_hash_content(c)
92
+ when Content::Base
93
+ c
94
+ else
95
+ raise InvalidItemError, "Unsupported content element: #{c.class}"
96
+ end
97
+ end
98
+ else
99
+ raise InvalidItemError, "Unsupported content type: #{content.class}"
100
+ end
101
+ end
102
+
103
+ def normalize_hash_content(hash)
104
+ hash = hash.transform_keys(&:to_s)
105
+ unless hash["type"]
106
+ raise InvalidItemError,
107
+ "Content hash is missing required \"type\" key: #{hash.inspect}"
108
+ end
109
+ Content::Base.from_h(hash)
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PromptBuilder
4
+ module Items
5
+ # Represents a reasoning item in a conversation. This captures the
6
+ # model's chain-of-thought reasoning output.
7
+ class Reasoning < Base
8
+ # @return [String, nil] the reasoning identifier
9
+ attr_reader :id
10
+
11
+ # @return [String, nil] the reasoning status
12
+ attr_reader :status
13
+
14
+ # @return [String, nil] the encrypted reasoning content
15
+ attr_reader :encrypted_content
16
+
17
+ # @return [Array<Hash>] summary blocks
18
+ attr_reader :summary
19
+
20
+ # @return [Array<Hash>] reasoning content blocks
21
+ attr_reader :content
22
+
23
+ # @return [Hash, nil] provider-specific extra data
24
+ attr_reader :extra
25
+
26
+ # Create a new Reasoning item.
27
+ #
28
+ # @param id [String, nil] the reasoning identifier
29
+ # @param status [String, nil] the reasoning status
30
+ # @param encrypted_content [String, nil] the encrypted reasoning content
31
+ # @param summary [Array<Hash>] summary blocks
32
+ # @param content [Array<Hash>] reasoning content blocks
33
+ # @param extra [Hash] provider-specific extra keyword arguments
34
+ def initialize(id: nil, status: nil, encrypted_content: nil, summary: [], content: [], **extra)
35
+ @id = id&.to_s
36
+ @status = status&.to_s
37
+ @encrypted_content = encrypted_content&.to_s
38
+ @summary = PromptBuilder.jsonify(summary)
39
+ @content = PromptBuilder.jsonify(content)
40
+ @extra = extra.transform_keys(&:to_s)
41
+ end
42
+
43
+ class << self
44
+ # Deserialize a Reasoning item from a Hash.
45
+ #
46
+ # @param hash [Hash] a Hash with string keys
47
+ # @return [Reasoning]
48
+ def from_h(hash)
49
+ new(
50
+ id: hash["id"],
51
+ status: hash["status"],
52
+ encrypted_content: hash["encrypted_content"],
53
+ summary: hash["summary"] || [],
54
+ content: hash["content"] || [],
55
+ **hash.except("type", "id", "status", "encrypted_content", "summary", "content").transform_keys(&:to_sym)
56
+ )
57
+ end
58
+ end
59
+
60
+ # Serialize to a Hash with string keys. Nil and empty values are omitted.
61
+ #
62
+ # @return [Hash]
63
+ def to_h
64
+ h = {"type" => "reasoning"}
65
+ h["id"] = @id if @id
66
+ h["status"] = @status if @status
67
+ h["encrypted_content"] = @encrypted_content if @encrypted_content
68
+ h["summary"] = @summary unless @summary.empty?
69
+ h["content"] = @content unless @content.empty?
70
+ h = PromptBuilder.jsonify(@extra).merge(h) unless @extra.empty?
71
+ h
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PromptBuilder
4
+ module Items
5
+ autoload :Base, File.expand_path("items/base", __dir__)
6
+ autoload :Compaction, File.expand_path("items/compaction", __dir__)
7
+ autoload :FunctionCall, File.expand_path("items/function_call", __dir__)
8
+ autoload :FunctionCallOutput, File.expand_path("items/function_call_output", __dir__)
9
+ autoload :ItemReference, File.expand_path("items/item_reference", __dir__)
10
+ autoload :Message, File.expand_path("items/message", __dir__)
11
+ autoload :Reasoning, File.expand_path("items/reasoning", __dir__)
12
+ end
13
+ end