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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +24 -0
- data/MIT-LICENSE +20 -0
- data/README.md +763 -0
- data/VERSION +1 -0
- data/lib/prompt_builder/content/base.rb +44 -0
- data/lib/prompt_builder/content/input_file.rb +63 -0
- data/lib/prompt_builder/content/input_image.rb +64 -0
- data/lib/prompt_builder/content/input_text.rb +42 -0
- data/lib/prompt_builder/content/input_video.rb +43 -0
- data/lib/prompt_builder/content/output_text.rb +59 -0
- data/lib/prompt_builder/content/reasoning_text.rb +42 -0
- data/lib/prompt_builder/content/refusal_content.rb +42 -0
- data/lib/prompt_builder/content/summary_text.rb +42 -0
- data/lib/prompt_builder/content/text.rb +42 -0
- data/lib/prompt_builder/content.rb +28 -0
- data/lib/prompt_builder/errors.rb +18 -0
- data/lib/prompt_builder/items/base.rb +41 -0
- data/lib/prompt_builder/items/compaction.rb +60 -0
- data/lib/prompt_builder/items/function_call.rb +97 -0
- data/lib/prompt_builder/items/function_call_output.rb +110 -0
- data/lib/prompt_builder/items/item_reference.rb +42 -0
- data/lib/prompt_builder/items/message.rb +113 -0
- data/lib/prompt_builder/items/reasoning.rb +75 -0
- data/lib/prompt_builder/items.rb +13 -0
- data/lib/prompt_builder/response.rb +257 -0
- data/lib/prompt_builder/serializers/base.rb +37 -0
- data/lib/prompt_builder/serializers/chat_completion/request.rb +389 -0
- data/lib/prompt_builder/serializers/chat_completion/response.rb +139 -0
- data/lib/prompt_builder/serializers/chat_completion.rb +30 -0
- data/lib/prompt_builder/serializers/converse/request.rb +623 -0
- data/lib/prompt_builder/serializers/converse/response.rb +140 -0
- data/lib/prompt_builder/serializers/converse.rb +30 -0
- data/lib/prompt_builder/serializers/gemini/request.rb +562 -0
- data/lib/prompt_builder/serializers/gemini/response.rb +233 -0
- data/lib/prompt_builder/serializers/gemini.rb +30 -0
- data/lib/prompt_builder/serializers/messages/request.rb +634 -0
- data/lib/prompt_builder/serializers/messages/response.rb +157 -0
- data/lib/prompt_builder/serializers/messages.rb +30 -0
- data/lib/prompt_builder/serializers/open_responses/request.rb +229 -0
- data/lib/prompt_builder/serializers/open_responses/response.rb +18 -0
- data/lib/prompt_builder/serializers/open_responses.rb +30 -0
- data/lib/prompt_builder/serializers.rb +35 -0
- data/lib/prompt_builder/session.rb +383 -0
- data/lib/prompt_builder/tool_registry.rb +75 -0
- data/lib/prompt_builder/tools/definition.rb +66 -0
- data/lib/prompt_builder/tools.rb +7 -0
- data/lib/prompt_builder/usage.rb +100 -0
- data/lib/prompt_builder.rb +86 -0
- data/prompt_builder.gemspec +41 -0
- metadata +107 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PromptBuilder
|
|
4
|
+
module Serializers
|
|
5
|
+
class ChatCompletion < Base
|
|
6
|
+
# Response parser for the OpenAI Chat Completions API format.
|
|
7
|
+
#
|
|
8
|
+
# Responses with multiple choices (+n > 1+) parse only the first choice;
|
|
9
|
+
# the canonical response object has no candidate collection. Audio
|
|
10
|
+
# responses, legacy +function_call+ responses, non-function tool calls,
|
|
11
|
+
# and unknown content block types are silently skipped. Streaming chunks
|
|
12
|
+
# (+chat.completion.chunk+ deltas) raise +UnsupportedFormatError+ — this
|
|
13
|
+
# parser expects a fully assembled non-streaming response body.
|
|
14
|
+
# Top-level +system_fingerprint+ is exposed through +extra+.
|
|
15
|
+
class Response < Base
|
|
16
|
+
class << self
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def deserialize_response(hash)
|
|
20
|
+
validate_response!(hash)
|
|
21
|
+
|
|
22
|
+
output = []
|
|
23
|
+
choice = hash.dig("choices", 0)
|
|
24
|
+
|
|
25
|
+
if choice
|
|
26
|
+
message = choice["message"] || {}
|
|
27
|
+
logprobs_content = choice.dig("logprobs", "content") || []
|
|
28
|
+
annotations = message["annotations"] || []
|
|
29
|
+
|
|
30
|
+
# Audio responses and legacy function_call responses are not
|
|
31
|
+
# modeled; they are silently ignored.
|
|
32
|
+
|
|
33
|
+
if message["refusal"]
|
|
34
|
+
output << Items::Message.new(
|
|
35
|
+
role: "assistant",
|
|
36
|
+
content: [Content::RefusalContent.new(refusal: message["refusal"])]
|
|
37
|
+
)
|
|
38
|
+
elsif message["content"].is_a?(Array)
|
|
39
|
+
contents = message["content"].each_with_object([]) do |block, acc|
|
|
40
|
+
case block["type"]
|
|
41
|
+
when "text", nil
|
|
42
|
+
text = block["text"] || ""
|
|
43
|
+
next if text.empty?
|
|
44
|
+
|
|
45
|
+
acc << Content::OutputText.new(
|
|
46
|
+
text: text,
|
|
47
|
+
logprobs: logprobs_content,
|
|
48
|
+
annotations: annotations
|
|
49
|
+
)
|
|
50
|
+
when "refusal"
|
|
51
|
+
acc << Content::RefusalContent.new(refusal: block["refusal"]) if block["refusal"]
|
|
52
|
+
else
|
|
53
|
+
# Unsupported content block types are silently skipped.
|
|
54
|
+
next
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
output << Items::Message.new(role: "assistant", content: contents) unless contents.empty?
|
|
58
|
+
elsif message["content"] && !message["content"].empty?
|
|
59
|
+
output << Items::Message.new(
|
|
60
|
+
role: "assistant",
|
|
61
|
+
content: [Content::OutputText.new(
|
|
62
|
+
text: message["content"],
|
|
63
|
+
logprobs: logprobs_content,
|
|
64
|
+
annotations: annotations
|
|
65
|
+
)]
|
|
66
|
+
)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
(message["tool_calls"] || []).each do |tool_call|
|
|
70
|
+
# Non-function tool call types are silently skipped.
|
|
71
|
+
next unless tool_call["type"] == "function"
|
|
72
|
+
|
|
73
|
+
function = tool_call["function"] || {}
|
|
74
|
+
output << Items::FunctionCall.new(
|
|
75
|
+
name: function["name"],
|
|
76
|
+
call_id: tool_call["id"],
|
|
77
|
+
arguments: function["arguments"] || "{}"
|
|
78
|
+
)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
usage_hash = hash["usage"]
|
|
83
|
+
usage = if usage_hash
|
|
84
|
+
Usage.new(
|
|
85
|
+
input_tokens: usage_hash["prompt_tokens"],
|
|
86
|
+
output_tokens: usage_hash["completion_tokens"],
|
|
87
|
+
total_tokens: usage_hash["total_tokens"],
|
|
88
|
+
input_tokens_details: usage_hash["prompt_tokens_details"],
|
|
89
|
+
output_tokens_details: usage_hash["completion_tokens_details"]
|
|
90
|
+
)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
PromptBuilder::Response.new(
|
|
94
|
+
id: hash["id"],
|
|
95
|
+
object: hash["object"],
|
|
96
|
+
created_at: hash["created"],
|
|
97
|
+
model: hash["model"],
|
|
98
|
+
service_tier: hash["service_tier"],
|
|
99
|
+
output: output,
|
|
100
|
+
status: map_finish_reason(choice&.dig("finish_reason")),
|
|
101
|
+
usage: usage,
|
|
102
|
+
extra: provider_data(hash)
|
|
103
|
+
)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def validate_response!(hash)
|
|
107
|
+
# A streaming chunk is not a complete response body, so it cannot be
|
|
108
|
+
# parsed into a canonical Response.
|
|
109
|
+
if hash["object"] == "chat.completion.chunk"
|
|
110
|
+
raise UnsupportedFormatError, "Chat Completions streaming chunks are not supported"
|
|
111
|
+
end
|
|
112
|
+
# Responses with multiple choices have no canonical multi-candidate
|
|
113
|
+
# representation; only the first choice is parsed (handled by the
|
|
114
|
+
# caller using choices[0]).
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def provider_data(hash)
|
|
118
|
+
data = {}
|
|
119
|
+
data["system_fingerprint"] = hash["system_fingerprint"] if hash["system_fingerprint"]
|
|
120
|
+
data.empty? ? nil : data
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def map_finish_reason(reason)
|
|
124
|
+
case reason
|
|
125
|
+
when "stop", "tool_calls", "function_call"
|
|
126
|
+
"completed"
|
|
127
|
+
when "length"
|
|
128
|
+
"incomplete"
|
|
129
|
+
when "content_filter"
|
|
130
|
+
"failed"
|
|
131
|
+
else
|
|
132
|
+
reason
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PromptBuilder
|
|
4
|
+
module Serializers
|
|
5
|
+
# Serializer for the OpenAI Chat Completions API format.
|
|
6
|
+
# Delegates request and response handling to dedicated nested classes.
|
|
7
|
+
class ChatCompletion < Base
|
|
8
|
+
autoload :Request, File.expand_path("chat_completion/request", __dir__)
|
|
9
|
+
autoload :Response, File.expand_path("chat_completion/response", __dir__)
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
# Export a session to Chat Completions request payload.
|
|
13
|
+
#
|
|
14
|
+
# @param session [Session] the session to export
|
|
15
|
+
# @return [Hash] the serialized request payload
|
|
16
|
+
def request_payload(session)
|
|
17
|
+
Request.request_payload(session)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Parse a Chat Completions response into an PromptBuilder::Response.
|
|
21
|
+
#
|
|
22
|
+
# @param hash [Hash] the response hash in Chat Completions format
|
|
23
|
+
# @return [PromptBuilder::Response] the parsed response
|
|
24
|
+
def parse_response(hash)
|
|
25
|
+
Response.parse_response(hash)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|