gpt 0.0.1 → 0.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 63a792b425c4cbce7e97eea48da74efc7d1fab57bb88f8623c683b19d567a55c
4
- data.tar.gz: d359d30ae5e9d184330a7922d39416ab80de88a03871a5f6009d2e9d9c65b562
3
+ metadata.gz: 840186b5978b9ff54b0aa11256b238bfe03dba6d50020e70ace54dc9ad69e95c
4
+ data.tar.gz: 63ea299d5a2d5f0c119e99605cde359294bc60de72fe84eadfd704288c03eb06
5
5
  SHA512:
6
- metadata.gz: 87a5832356464d22b9e0b10a346e8e441593ca6a54ba81a76f135b48cb94fe01951fe89954bcff891eba3d2bfae5a0755c99a974900b90fb4615dfe0459ae410
7
- data.tar.gz: 4ad05661b2149c090d5e2546d3952b9410cd9d6c0332cca32cfed5499b21972a0953a153768ebff59ddc66458b1122cadf2312ce45fff6a292c0d7b5a43a6da6
6
+ metadata.gz: 7aa46b3f49131432ee7e33303d9581a391d7eea15e75b9ff7e5e1ef78ed33a5c720f337cdafb9641d62a8a11bac427b222fcaa8e9d473f4040559cd7c6e8769c
7
+ data.tar.gz: f8a4865378bd4981362d751e9c4140618bb2451b90c408a92a3ba95ee391138168c3fe26dcbe73211b867d7547b3ac6803f57b5a989d19e4021488e4c5365e74
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.0
4
+ - Suporte e exemplos para GPT-5 (reasoning minimal, text verbosity, custom tools e allowed_tools)
5
+ - Validação de `OPENAI_API_KEY` e ajuste de User-Agent
6
+ - README atualizado para GPT-5
7
+
8
+ ## 0.1.1
9
+ - Adiciona `GPT.ask` para uso simplificado, com suporte a streaming
10
+ - Adiciona `GPT::ResponseExtender` com helpers (`content`, `usage`, `total_tokens`, `to_h`)
11
+ - `Responses#create/get` passam a estender a resposta com os helpers
12
+
3
13
  ## 0.0.1
4
14
  - Primeira versão com cliente para API Responses (create/get/delete/cancel/input_items) e streaming SSE.
5
15
 
data/README.md CHANGED
@@ -1,39 +1,93 @@
1
1
  # gpt
2
2
 
3
- Cliente Ruby simples para a API Responses.
3
+ Cliente Ruby simples para a Responses API, com foco no GPT-5, com uma API de alto nível inspirada no OpenAIExt.
4
4
 
5
- Instalação
5
+ ## Instalação
6
6
  ```
7
- gem build gpt.gemspec && gem install ./gpt-0.0.1.gem
7
+ bash build_and_install.sh
8
8
  ```
9
9
 
10
- Uso básico
10
+ ## Configuração
11
+ - Defina `OPENAI_API_KEY` ou `OPENAI_ACCESS_TOKEN` no ambiente.
12
+ - Opcional: `OPENAI_ORG_ID` ou `OPENAI_ORGANIZATION_ID`, `OPENAI_PROJECT_ID`.
13
+ - Opcional: `OPENAI_REQUEST_TIMEOUT` (segundos, padrão 120).
14
+
15
+ ## Uso básico (GPT-5)
11
16
  ```ruby
12
17
  require 'gpt'
13
18
 
14
- client = GPT.client
19
+ res = GPT.ask('Diga olá em uma frase.', model: 'gpt-5')
20
+ puts res.content
21
+ ```
22
+
23
+ ## Reasoning mínimo (minimal)
24
+ ```ruby
25
+ res = GPT.responses.create({
26
+ 'model' => 'gpt-5',
27
+ 'input' => 'Quanto ouro seria necessário para cobrir a Estátua da Liberdade com 1mm?',
28
+ 'reasoning' => { 'effort' => 'minimal' }
29
+ })
30
+ ```
31
+
32
+ ## Verbosidade baixa
33
+ ```ruby
34
+ res = GPT.responses.create({
35
+ 'model' => 'gpt-5',
36
+ 'input' => 'Qual é a resposta para a vida, o universo e tudo mais?',
37
+ 'text' => { 'verbosity' => 'low' }
38
+ })
39
+ ```
40
+
41
+ ## Ferramentas personalizadas (custom tools)
42
+ ```ruby
43
+ res = GPT.responses.create({
44
+ 'model' => 'gpt-5',
45
+ 'input' => 'Use a ferramenta code_exec para calcular a área de um círculo com raio igual ao número de letras r em blueberry',
46
+ 'tools' => [
47
+ { 'type' => 'custom', 'name' => 'code_exec', 'description' => 'Executa código Python arbitrário' }
48
+ ]
49
+ })
50
+ ```
51
+
52
+ ## Restringindo ferramentas (allowed_tools)
53
+ ```ruby
54
+ res = GPT.responses.create({
55
+ 'model' => 'gpt-5',
56
+ 'input' => 'Como está o tempo em São Paulo?',
57
+ 'tools' => [ { 'type' => 'function', 'name' => 'get_weather' } ],
58
+ 'tool_choice' => {
59
+ 'type' => 'allowed_tools',
60
+ 'mode' => 'auto',
61
+ 'tools' => [ { 'type' => 'function', 'name' => 'get_weather' } ]
62
+ }
63
+ })
64
+ ```
15
65
 
16
- res = client.responses.create({
17
- 'model' => 'gpt-4.1',
18
- 'input' => 'Diga olá em uma frase.'
66
+ ## Passando raciocínio prévio (previous_response_id)
67
+ ```ruby
68
+ first = GPT.responses.create({
69
+ 'model' => 'gpt-5',
70
+ 'input' => 'Planeje passos para resolver X.'
19
71
  })
20
72
 
21
- puts res['id']
73
+ followup = GPT.responses.create({
74
+ 'model' => 'gpt-5',
75
+ 'input' => 'Agora execute o primeiro passo.',
76
+ 'previous_response_id' => first['id']
77
+ })
22
78
  ```
23
79
 
24
- Streaming SSE
80
+ ## Streaming SSE
25
81
  ```ruby
26
82
  require 'gpt'
27
83
 
28
- GPT.responses.stream({
29
- 'model' => 'gpt-4.1',
30
- 'input' => 'Conte uma história curta.'
31
- }) do |chunk|
32
- print chunk
33
- end
84
+ GPT.ask('Conte uma história curta.', model: 'gpt-5', stream: true) { |chunk| print chunk }
85
+
86
+ # Streaming de texto direto
87
+ GPT.ask('Conte uma história curta.', model: 'gpt-5', text_stream: true) { |text| print text }
34
88
  ```
35
89
 
36
- Outras operações
90
+ ## Outras operações
37
91
  ```ruby
38
92
  id = res['id']
39
93
  GPT.responses.get(id)
@@ -41,3 +95,12 @@ GPT.responses.input_items(id)
41
95
  GPT.responses.cancel(id)
42
96
  GPT.responses.delete(id)
43
97
  ```
98
+
99
+ ## Helpers de resposta
100
+ ```ruby
101
+ res = GPT.ask('Qual a capital da França?', model: 'gpt-5')
102
+ res.content
103
+ res.model
104
+ res.total_tokens
105
+ res.to_h
106
+ ```
data/lib/gpt/client.rb CHANGED
@@ -5,10 +5,10 @@ module GPT
5
5
 
6
6
  attr_reader :api_key, :base_url, :timeout, :organization, :project
7
7
 
8
- def initialize(api_key: ENV['OPENAI_API_KEY'], base_url: nil, timeout: nil, organization: ENV['OPENAI_ORG_ID'], project: ENV['OPENAI_PROJECT_ID'])
8
+ def initialize(api_key: ENV['OPENAI_API_KEY'] || ENV['OPENAI_ACCESS_TOKEN'], base_url: nil, timeout: nil, organization: ENV['OPENAI_ORG_ID'] || ENV['OPENAI_ORGANIZATION_ID'], project: ENV['OPENAI_PROJECT_ID'])
9
9
  @api_key = api_key
10
10
  @base_url = base_url || DEFAULT_BASE_URL
11
- @timeout = (timeout || DEFAULT_TIMEOUT).to_i
11
+ @timeout = (timeout || ENV['OPENAI_REQUEST_TIMEOUT'] || DEFAULT_TIMEOUT).to_i
12
12
  @organization = organization
13
13
  @project = project
14
14
  end
@@ -86,10 +86,13 @@ module GPT
86
86
  end
87
87
 
88
88
  def apply_headers(req)
89
+ if !api_key || api_key.empty?
90
+ raise GPT::Error.new('Defina OPENAI_API_KEY ou OPENAI_ACCESS_TOKEN')
91
+ end
89
92
  req['Authorization'] = "Bearer #{api_key}"
90
93
  req['OpenAI-Organization'] = organization if organization && !organization.empty?
91
94
  req['OpenAI-Project'] = project if project && !project.empty?
92
- req['User-Agent'] = 'gpt-ruby/0.0.1'
95
+ req['User-Agent'] = "gpt-ruby/#{GPT::VERSION}"
93
96
  end
94
97
 
95
98
  def parse_response(res)
@@ -0,0 +1,74 @@
1
+ module GPT
2
+ module ResponseExtender
3
+ def message
4
+ if self['choices']
5
+ dig('choices', 0, 'message') || {}
6
+ else
7
+ output_message = if self['output'].is_a?(Array)
8
+ self['output'].find { |i| i['type'] == 'message' } || self['output'].first
9
+ end
10
+ output_message || {}
11
+ end
12
+ end
13
+
14
+ def content
15
+ if self['choices']
16
+ dig('choices', 0, 'message', 'content')
17
+ else
18
+ msg = message
19
+ contents = msg && msg['content']
20
+ return nil unless contents.is_a?(Array)
21
+ text_item = contents.find { |c| c['type'] == 'output_text' || c['type'] == 'text' }
22
+ text_item && text_item['text']
23
+ end
24
+ end
25
+
26
+ def content?
27
+ !content.nil? && !content.empty?
28
+ end
29
+
30
+ def usage
31
+ self['usage'] || {}
32
+ end
33
+
34
+ def prompt_tokens
35
+ usage['prompt_tokens'] || 0
36
+ end
37
+
38
+ def completion_tokens
39
+ usage['completion_tokens'] || 0
40
+ end
41
+
42
+ def total_tokens
43
+ usage['total_tokens'] || 0
44
+ end
45
+
46
+ def model
47
+ self['model']
48
+ end
49
+
50
+ def created_at
51
+ if self['created']
52
+ Time.at(self['created'])
53
+ elsif self['created_at']
54
+ Time.at(self['created_at'])
55
+ end
56
+ end
57
+
58
+ def to_h
59
+ {
60
+ content: content,
61
+ role: message['role'],
62
+ model: model,
63
+ usage: usage,
64
+ created_at: created_at
65
+ }.compact
66
+ end
67
+
68
+ def to_s
69
+ content || '[No content]'
70
+ end
71
+ end
72
+ end
73
+
74
+
data/lib/gpt/responses.rb CHANGED
@@ -5,7 +5,9 @@ module GPT
5
5
  end
6
6
 
7
7
  def create(payload)
8
- @client.json_post('/v1/responses', body: payload)
8
+ res = @client.json_post('/v1/responses', body: payload)
9
+ res.extend(GPT::ResponseExtender) if res.is_a?(Hash)
10
+ res
9
11
  end
10
12
 
11
13
  def get(response_id, include: nil, include_obfuscation: nil, starting_after: nil, stream: nil)
@@ -14,7 +16,9 @@ module GPT
14
16
  query['include_obfuscation'] = include_obfuscation unless include_obfuscation.nil?
15
17
  query['starting_after'] = starting_after if starting_after
16
18
  query['stream'] = stream unless stream.nil?
17
- @client.json_get("/v1/responses/#{response_id}", query: query)
19
+ res = @client.json_get("/v1/responses/#{response_id}", query: query)
20
+ res.extend(GPT::ResponseExtender) if res.is_a?(Hash)
21
+ res
18
22
  end
19
23
 
20
24
  def delete(response_id)
@@ -42,6 +46,49 @@ module GPT
42
46
  yield chunk if block_given?
43
47
  end
44
48
  end
49
+
50
+ def stream_text(payload)
51
+ buffer = ''.dup
52
+ stream(payload) do |chunk|
53
+ buffer << chunk
54
+ parts = buffer.split("\n\n", -1)
55
+ buffer = parts.pop || ''.dup
56
+ parts.each do |raw_event|
57
+ lines = raw_event.split("\n")
58
+ event_name = nil
59
+ data_lines = []
60
+ lines.each do |line|
61
+ if line.start_with?('event:')
62
+ event_name = line.sub('event:', '').strip
63
+ elsif line.start_with?('data:')
64
+ data_lines << line.sub('data:', '').strip
65
+ end
66
+ end
67
+ next if data_lines.empty?
68
+ data = data_lines.join("\n")
69
+ next if data == '[DONE]'
70
+ begin
71
+ json = Oj.load(data)
72
+ rescue Oj::ParseError
73
+ next
74
+ end
75
+ case event_name
76
+ when 'response.output_text.delta'
77
+ delta = json['delta']
78
+ yield delta if delta && !delta.empty?
79
+ when 'response.delta'
80
+ delta = json.dig('delta', 'content')
81
+ if delta.is_a?(Array)
82
+ text_piece = delta.find { |c| c['type'] == 'output_text' || c['type'] == 'text' }
83
+ yield(text_piece['text']) if text_piece && text_piece['text'] && !text_piece['text'].empty?
84
+ end
85
+ else
86
+ # ignore other events
87
+ end
88
+ end
89
+ end
90
+ true
91
+ end
45
92
  end
46
93
  end
47
94
 
@@ -0,0 +1,5 @@
1
+ module GPT
2
+ VERSION = '0.1.1'
3
+ end
4
+
5
+
data/lib/gpt.rb CHANGED
@@ -1,10 +1,14 @@
1
+ module GPT; end
2
+
1
3
  require 'oj'
2
4
  require 'net/http'
3
5
  require 'uri'
4
6
 
7
+ require_relative 'gpt/version'
5
8
  require_relative 'gpt/error'
6
9
  require_relative 'gpt/client'
7
10
  require_relative 'gpt/responses'
11
+ require_relative 'gpt/response_extender'
8
12
 
9
13
  module GPT
10
14
  def self.client
@@ -14,6 +18,24 @@ module GPT
14
18
  def self.responses
15
19
  @responses ||= Responses.new(client)
16
20
  end
21
+
22
+ def self.ask(prompt, model: 'gpt-5', stream: false, text_stream: false, **opts, &block)
23
+ payload = { 'model' => model, 'input' => prompt }
24
+ opts.each { |k, v| payload[k.to_s] = v }
25
+ if stream
26
+ responses.stream(payload) do |chunk|
27
+ yield chunk if block_given?
28
+ end
29
+ elsif text_stream
30
+ responses.stream_text(payload) do |text|
31
+ yield text if block_given?
32
+ end
33
+ else
34
+ res = responses.create(payload)
35
+ res.extend(ResponseExtender)
36
+ res
37
+ end
38
+ end
17
39
  end
18
40
 
19
41
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gpt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gedean Dias
@@ -93,8 +93,8 @@ dependencies:
93
93
  - - "~>"
94
94
  - !ruby/object:Gem::Version
95
95
  version: '0.9'
96
- description: Based on ruby-openai, adds some extra features for working with OpenAI
97
- APIs
96
+ description: Cliente Ruby simples para a Responses API com suporte aos recursos do
97
+ GPT-5 (reasoning, verbosity, tools).
98
98
  email: gedean.dias@gmail.com
99
99
  executables: []
100
100
  extensions: []
@@ -106,7 +106,9 @@ files:
106
106
  - lib/gpt.rb
107
107
  - lib/gpt/client.rb
108
108
  - lib/gpt/error.rb
109
+ - lib/gpt/response_extender.rb
109
110
  - lib/gpt/responses.rb
111
+ - lib/gpt/version.rb
110
112
  homepage: https://github.com/gedean/openaiext
111
113
  licenses:
112
114
  - MIT
@@ -127,5 +129,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
127
129
  requirements: []
128
130
  rubygems_version: 3.7.1
129
131
  specification_version: 4
130
- summary: GPT >= 5
132
+ summary: Cliente Ruby para GPT-5 (Responses API)
131
133
  test_files: []