Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move HTTP Logging to Custom #29

Merged
merged 2 commits into from
Jun 21, 2024
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
6 changes: 6 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ AllCops:
NewCops: enable
TargetRubyVersion: 3.3

Layout/MultilineMethodCallIndentation:
EnforcedStyle: indented

Layout/FirstHashElementIndentation:
EnforcedStyle: consistent

Expand All @@ -18,6 +21,9 @@ Metrics/ParameterLists:
RSpec/NestedGroups:
Enabled: false

RSpec/MultipleMemoizedHelpers:
Enabled: false

RSpec/SpecFilePathFormat:
CustomTransform:
OmniAI: omniai
Expand Down
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:
omniai (1.3.0)
omniai (1.3.1)
event_stream_parser
http
zeitwerk
Expand Down
33 changes: 2 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,40 +87,11 @@ client = OmniAI::Example::Client.new(logger:)
```

```
I, [...] INFO -- : > POST https://...
D, [...] DEBUG -- : Authorization: Bearer ...
[INFO]: POST https://...
[INFO]: 200 OK
...
{"messages":[{"role":"user","content":"Tell me a joke!"}],"model":"..."}
I, [...] INFO -- : < 200 OK
D, [...] DEBUG -- : Date: ...
...
{
"id": "...",
"object": "...",
...
}
```

The level of the logger can be configured to either `INFO` and `DEBUG`:

**INFO**:

```ruby
logger.level = Logger::INFO
```

- Request: verb / URI
- Response: status

**DEBUG**:

```ruby
logger.level = Logger::DEBUG
```

- Request: verb / URI / headers / body
- Response: status / headers / body

#### Timeouts

Timeouts are configurable by passing a `timeout` an integer duration for the request / response of any APIs using:
Expand Down
1 change: 1 addition & 0 deletions lib/omniai/chat.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ def initialize(messages, client:, model:, temperature: nil, stream: nil, format:
# @raise [HTTPError]
def process!
response = request!

raise HTTPError, response.flush unless response.status.ok?

parse!(response:)
Expand Down
2 changes: 1 addition & 1 deletion lib/omniai/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def masked_api_key
# @return [HTTP::Client]
def connection
http = HTTP.persistent(@host)
http = http.use(logging: { logger: @logger }) if @logger
http = http.use(instrumentation: { instrumenter: Instrumentation.new(logger: @logger) }) if @logger
http = http.timeout(@timeout) if @timeout
http
end
Expand Down
37 changes: 37 additions & 0 deletions lib/omniai/instrumentation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# frozen_string_literal: true

module OmniAI
# Used for logging.
class Instrumentation
# @param logger [Logger]
def initialize(logger:)
@logger = logger
end

# @param name [String]
# @param payload [Hash]
# @option payload [Exception] :error
def instrument(name, payload = {})
error = payload[:error]
return unless error

@logger.error("#{name}: #{error.message}")
end

# @param name [String]
# @param payload [Hash]
# @option payload [HTTP::Request] :request
def start(_, payload)
request = payload[:request]
@logger.info("#{request.verb.upcase} #{request.uri}")
end

# @param name [String]
# @param payload [Hash]
# @option payload [HTTP::Response] :response
def finish(_, payload)
response = payload[:response]
@logger.info("#{response.status.code} #{response.status.reason}")
end
end
end
1 change: 1 addition & 0 deletions lib/omniai/speak.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ def initialize(input, client:, model:, voice:, speed: nil, format: nil)
# @return [Tempfile]
def process!(&block)
response = request!

raise HTTPError, response.flush unless response.status.ok?

if block
Expand Down
1 change: 1 addition & 0 deletions lib/omniai/transcribe.rb
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ def initialize(io, client:, model:, language: nil, prompt: nil, temperature: nil
# @return [OmniAI::Transcribe::Transcription]
def process!
response = request!

raise HTTPError, response.flush unless response.status.ok?

text = @format.nil? || @format.eql?(Format::JSON) ? response.parse['text'] : String(response.body)
Expand Down
2 changes: 1 addition & 1 deletion lib/omniai/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module OmniAI
VERSION = '1.3.0'
VERSION = '1.3.1'
end
2 changes: 1 addition & 1 deletion spec/omniai/client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
let(:api_key) { 'abcdef' }
let(:host) { 'http://localhost:8080' }
let(:timeout) { 5 }
let(:logger) { Logger.new($stdout) }
let(:logger) { instance_double(Logger) }

describe '#api_key' do
it { expect(client.api_key).to eq(api_key) }
Expand Down
43 changes: 43 additions & 0 deletions spec/omniai/instrumentation_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# frozen_string_literal: true

RSpec.describe OmniAI::Instrumentation do
subject(:instrumentation) { described_class.new(logger:) }

let(:logger) { instance_double(Logger) }

let(:response) { instance_double(HTTP::Response, request:, status:) }
let(:request) { instance_double(HTTP::Request, uri: '/chat', verb: 'POST') }
let(:status) { instance_double(HTTP::Response::Status, code: 200, reason: 'OK') }

describe '#instrument' do
subject(:instrument) { instrumentation.instrument('Error', error: StandardError.new('unknown')) }

context 'with an error' do
it 'logs the error' do
allow(logger).to receive(:error)
instrument
expect(logger).to have_received(:error).with('Error: unknown')
end
end
end

describe '#start' do
subject(:start) { instrumentation.start('start', request:) }

it 'logs the request' do
allow(logger).to receive(:info)
start
expect(logger).to have_received(:info).with('POST /chat')
end
end

describe '#finish' do
subject(:finish) { instrumentation.finish('finish', response:) }

it 'logs the response' do
allow(logger).to receive(:info)
finish
expect(logger).to have_received(:info).with('200 OK')
end
end
end