omniai-anthropic 1.6.3 → 1.8.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 +4 -4
- data/README.md +6 -6
- data/lib/omniai/anthropic/chat/choice_serializer.rb +25 -0
- data/lib/omniai/anthropic/chat/content_serializer.rb +20 -0
- data/lib/omniai/anthropic/chat/function_serializer.rb +27 -0
- data/lib/omniai/anthropic/chat/media_serializer.rb +23 -0
- data/lib/omniai/anthropic/chat/payload_serializer.rb +30 -0
- data/lib/omniai/anthropic/chat/stream.rb +94 -0
- data/lib/omniai/anthropic/chat/text_serializer.rb +22 -0
- data/lib/omniai/anthropic/chat/tool_call_serializer.rb +29 -0
- data/lib/omniai/anthropic/chat.rb +27 -22
- data/lib/omniai/anthropic/version.rb +1 -1
- metadata +10 -4
- data/lib/omniai/anthropic/chat/response/completion.rb +0 -29
- data/lib/omniai/anthropic/chat/response/stream.rb +0 -111
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 40dd84f42676e7000caed3c06c942ab1bca0d3d10763c279e084e5d378930946
|
4
|
+
data.tar.gz: 2233860b1769b297af92fe5c1156fb08ffb61a88452ba39d8ad79e131ae861be
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d4c99af8092f169086859edf43d6a9164d410caa0166a43fd2ff0b76156fb28cedca8fbbd84251e3405406336a371a068fd00968d93dc723050950dae4da7c80
|
7
|
+
data.tar.gz: f022585d04e3983ed15ec31cfc3b125bc5e6396be2d417a9cc10af0e827e32a0a19c324062987a83be5cfd306c4659d5e702ae7c931511189518c546b4e31489
|
data/README.md
CHANGED
@@ -40,7 +40,7 @@ A chat completion is generated by passing in prompts using any a variety of form
|
|
40
40
|
|
41
41
|
```ruby
|
42
42
|
completion = client.chat('Tell me a joke!')
|
43
|
-
completion.
|
43
|
+
completion.text # 'Why did the chicken cross the road? To get to the other side.'
|
44
44
|
```
|
45
45
|
|
46
46
|
```ruby
|
@@ -48,7 +48,7 @@ completion = client.chat do |prompt|
|
|
48
48
|
prompt.system('You are a helpful assistant.')
|
49
49
|
prompt.user('What is the capital of Canada?')
|
50
50
|
end
|
51
|
-
completion.
|
51
|
+
completion.text # 'The capital of Canada is Ottawa.'
|
52
52
|
```
|
53
53
|
|
54
54
|
#### Model
|
@@ -57,7 +57,7 @@ completion.choice.message.content # 'The capital of Canada is Ottawa.'
|
|
57
57
|
|
58
58
|
```ruby
|
59
59
|
completion = client.chat('Provide code for fibonacci', model: OmniAI::Anthropic::Chat::Model::CLAUDE_SONNET)
|
60
|
-
completion.
|
60
|
+
completion.text # 'def fibonacci(n)...end'
|
61
61
|
```
|
62
62
|
|
63
63
|
[Anthropic API Reference `model`](https://docs.anthropic.com/en/api/messages)
|
@@ -68,7 +68,7 @@ completion.choice.message.content # 'def fibonacci(n)...end'
|
|
68
68
|
|
69
69
|
```ruby
|
70
70
|
completion = client.chat('Pick a number between 1 and 5', temperature: 1.0)
|
71
|
-
completion.
|
71
|
+
completion.text # '3'
|
72
72
|
```
|
73
73
|
|
74
74
|
[Anthropic API Reference `temperature`](https://docs.anthropic.com/en/api/messages)
|
@@ -79,7 +79,7 @@ completion.choice.message.content # '3'
|
|
79
79
|
|
80
80
|
```ruby
|
81
81
|
stream = proc do |chunk|
|
82
|
-
print(chunk.
|
82
|
+
print(chunk.text) # 'Better', 'three', 'hours', ...
|
83
83
|
end
|
84
84
|
client.chat('Be poetic.', stream:)
|
85
85
|
```
|
@@ -94,7 +94,7 @@ client.chat('Be poetic.', stream:)
|
|
94
94
|
completion = client.chat(format: :json) do |prompt|
|
95
95
|
prompt.system(OmniAI::Chat::JSON_PROMPT)
|
96
96
|
prompt.user('What is the name of the drummer for the Beatles?')
|
97
|
-
JSON.parse(completion.
|
97
|
+
JSON.parse(completion.text) # { "name": "Ringo" }
|
98
98
|
```
|
99
99
|
|
100
100
|
[Anthropic API Reference `control-output-format`](https://docs.anthropic.com/en/docs/control-output-format)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OmniAI
|
4
|
+
module Anthropic
|
5
|
+
class Chat
|
6
|
+
# Overrides choice serialize / deserialize.
|
7
|
+
module ChoiceSerializer
|
8
|
+
# @param choice [OmniAI::Chat::Choice]
|
9
|
+
# @param context [Context]
|
10
|
+
# @return [Hash]
|
11
|
+
def self.serialize(choice, context:)
|
12
|
+
choice.message.serialize(context:)
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param data [Hash]
|
16
|
+
# @param context [Context]
|
17
|
+
# @return [OmniAI::Chat::Choice]
|
18
|
+
def self.deserialize(data, context:)
|
19
|
+
message = OmniAI::Chat::Message.deserialize(data, context:)
|
20
|
+
OmniAI::Chat::Choice.new(message:)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OmniAI
|
4
|
+
module Anthropic
|
5
|
+
class Chat
|
6
|
+
# Overrides content serialize / deserialize.
|
7
|
+
module ContentSerializer
|
8
|
+
# @param data [Hash]
|
9
|
+
# @param context [Context]
|
10
|
+
# @return [OmniAI::Chat::Text, OmniAI::Chat::ToolCall]
|
11
|
+
def self.deserialize(data, context:)
|
12
|
+
case data['type']
|
13
|
+
when 'text' then OmniAI::Chat::Text.deserialize(data, context:)
|
14
|
+
when 'tool_use' then OmniAI::Chat::ToolCall.deserialize(data, context:)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OmniAI
|
4
|
+
module Anthropic
|
5
|
+
class Chat
|
6
|
+
# Overrides function serialize / deserialize.
|
7
|
+
module FunctionSerializer
|
8
|
+
# @param function [OmniAI::Chat::Function]
|
9
|
+
# @return [Hash]
|
10
|
+
def self.serialize(function, *)
|
11
|
+
{
|
12
|
+
name: function.name,
|
13
|
+
input: function.arguments,
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
# @param data [Hash]
|
18
|
+
# @return [OmniAI::Chat::Function]
|
19
|
+
def self.deserialize(data, *)
|
20
|
+
name = data['name']
|
21
|
+
arguments = data['input']
|
22
|
+
OmniAI::Chat::Function.new(name:, arguments:)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OmniAI
|
4
|
+
module Anthropic
|
5
|
+
class Chat
|
6
|
+
# Overrides media serialize / deserialize.
|
7
|
+
module MediaSerializer
|
8
|
+
# @param payload [OmniAI::Chat::Media]
|
9
|
+
# @return [Hash]
|
10
|
+
def self.serialize(media, *)
|
11
|
+
{
|
12
|
+
type: media.kind, # i.e. 'image' / 'video' / 'audio' / ...
|
13
|
+
source: {
|
14
|
+
type: 'base64',
|
15
|
+
media_type: media.type, # i.e. 'image/jpeg' / 'video/ogg' / 'audio/mpeg' / ...
|
16
|
+
data: media.data,
|
17
|
+
},
|
18
|
+
}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OmniAI
|
4
|
+
module Anthropic
|
5
|
+
class Chat
|
6
|
+
# Overrides payload serialize / deserialize.
|
7
|
+
module PayloadSerializer
|
8
|
+
# @param payload [OmniAI::Chat::Payload]
|
9
|
+
# @param context [OmniAI::Context]
|
10
|
+
# @return [Hash]
|
11
|
+
def self.serialize(payload, context:)
|
12
|
+
usage = payload.usage.serialize(context:)
|
13
|
+
choice = payload.choice.serialize(context:)
|
14
|
+
|
15
|
+
choice.merge({ usage: })
|
16
|
+
end
|
17
|
+
|
18
|
+
# @param data [Hash]
|
19
|
+
# @param context [OmniAI::Context]
|
20
|
+
# @return [OmniAI::Chat::Payload]
|
21
|
+
def self.deserialize(data, context:)
|
22
|
+
usage = OmniAI::Chat::Usage.deserialize(data['usage'], context:) if data['usage']
|
23
|
+
choice = OmniAI::Chat::Choice.deserialize(data, context:)
|
24
|
+
|
25
|
+
OmniAI::Chat::Payload.new(choices: [choice], usage:)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OmniAI
|
4
|
+
module Anthropic
|
5
|
+
class Chat
|
6
|
+
# A stream given when streaming.
|
7
|
+
class Stream < OmniAI::Chat::Stream
|
8
|
+
module Type
|
9
|
+
PING = 'ping'
|
10
|
+
MESSAGE_START = 'message_start'
|
11
|
+
MESSAGE_STOP = 'message_stop'
|
12
|
+
MESSAGE_DELTA = 'message_delta'
|
13
|
+
CONTENT_BLOCK_START = 'content_block_start'
|
14
|
+
CONTENT_BLOCK_STOP = 'content_block_stop'
|
15
|
+
CONTENT_BLOCK_DELTA = 'content_block_delta'
|
16
|
+
end
|
17
|
+
|
18
|
+
# Process the stream into chunks by event.
|
19
|
+
class Builder
|
20
|
+
# @return [OmniAI::Chat::Payload, nil]
|
21
|
+
def payload(context:)
|
22
|
+
return unless @content
|
23
|
+
|
24
|
+
OmniAI::Chat::Payload.deserialize(@message.merge({
|
25
|
+
'content' => @content,
|
26
|
+
}), context:)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Handler for Type::MESSAGE_START
|
30
|
+
#
|
31
|
+
# @param data [Hash]
|
32
|
+
def message_start(data)
|
33
|
+
@message = data['message']
|
34
|
+
end
|
35
|
+
|
36
|
+
# Handler for Type::MESSAGE_STOP
|
37
|
+
#
|
38
|
+
# @param _data [Hash]
|
39
|
+
def message_stop(_data)
|
40
|
+
@message = nil
|
41
|
+
end
|
42
|
+
|
43
|
+
# Handler for Type::CONTENT_BLOCK_START
|
44
|
+
#
|
45
|
+
# @param data [Hash]
|
46
|
+
def content_block_start(_data)
|
47
|
+
@content = nil
|
48
|
+
end
|
49
|
+
|
50
|
+
# Handler for Type::CONTENT_BLOCK_STOP
|
51
|
+
#
|
52
|
+
# @param _data [Hash]
|
53
|
+
def content_block_stop(_data)
|
54
|
+
@content = nil
|
55
|
+
end
|
56
|
+
|
57
|
+
# Handler for Type::CONTENT_BLOCK_DELTA
|
58
|
+
#
|
59
|
+
# @param data [Hash]
|
60
|
+
def content_block_delta(data)
|
61
|
+
@content = [{ 'type' => 'text', 'text' => data['delta']['text'] }]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
protected
|
66
|
+
|
67
|
+
def builder
|
68
|
+
@builder ||= Builder.new
|
69
|
+
end
|
70
|
+
|
71
|
+
# @param type [String]
|
72
|
+
# @param data [Hash]
|
73
|
+
# @param builder [Builder]
|
74
|
+
def process!(type, data, id, &block)
|
75
|
+
log(type, data, id)
|
76
|
+
|
77
|
+
data = JSON.parse(data)
|
78
|
+
|
79
|
+
case type
|
80
|
+
when Type::MESSAGE_START then builder.message_start(data)
|
81
|
+
when Type::CONTENT_BLOCK_START then builder.content_block_start(data)
|
82
|
+
when Type::CONTENT_BLOCK_STOP then builder.content_block_stop(data)
|
83
|
+
when Type::MESSAGE_STOP then builder.message_stop(data)
|
84
|
+
when Type::CONTENT_BLOCK_DELTA
|
85
|
+
builder.content_block_delta(data)
|
86
|
+
|
87
|
+
payload = builder.payload(context: @context)
|
88
|
+
block.call(payload) if payload
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OmniAI
|
4
|
+
module Anthropic
|
5
|
+
class Chat
|
6
|
+
# Overrides text serialize / deserialize.
|
7
|
+
module TextSerializer
|
8
|
+
# @param text [OmniAI::Chat::Text]
|
9
|
+
# @return [Hash]
|
10
|
+
def self.serialize(text, *)
|
11
|
+
{ type: 'text', text: text.text }
|
12
|
+
end
|
13
|
+
|
14
|
+
# @param data [Hash]
|
15
|
+
# @return [OmniAI::Chat::Text]
|
16
|
+
def self.deserialize(data, *)
|
17
|
+
OmniAI::Chat::Text.new(data['text'])
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OmniAI
|
4
|
+
module Anthropic
|
5
|
+
class Chat
|
6
|
+
# Overrides tool-call serialize / deserialize.
|
7
|
+
module ToolCallSerializer
|
8
|
+
# @param tool_call [OmniAI::Chat::ToolCall]
|
9
|
+
# @param context [OmniAI::Context]
|
10
|
+
# @return [Hash]
|
11
|
+
def self.serialize(tool_call, context:)
|
12
|
+
function = tool_call.function.serialize(context:)
|
13
|
+
{
|
14
|
+
id: tool_call.id,
|
15
|
+
type: 'tool_use',
|
16
|
+
}.merge(function)
|
17
|
+
end
|
18
|
+
|
19
|
+
# @param data [Hash]
|
20
|
+
# @param context [OmniAI::Context]
|
21
|
+
# @return [OmniAI::Chat::ToolCall]
|
22
|
+
def self.deserialize(data, context:)
|
23
|
+
function = OmniAI::Chat::Function.deserialize(data, context:)
|
24
|
+
OmniAI::Chat::ToolCall.new(id: data['id'], function:)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -10,7 +10,7 @@ module OmniAI
|
|
10
10
|
# prompt.system('You are an expert in the field of AI.')
|
11
11
|
# prompt.user('What are the biggest risks of AI?')
|
12
12
|
# end
|
13
|
-
# completion.
|
13
|
+
# completion.text # '...'
|
14
14
|
class Chat < OmniAI::Chat
|
15
15
|
module Model
|
16
16
|
CLAUDE_INSTANT_1_0 = 'claude-instant-1.2'
|
@@ -27,26 +27,22 @@ module OmniAI
|
|
27
27
|
|
28
28
|
DEFAULT_MODEL = Model::CLAUDE_SONNET
|
29
29
|
|
30
|
-
# @param [Media]
|
31
|
-
# @return [Hash]
|
32
|
-
# @example
|
33
|
-
# media = Media.new(...)
|
34
|
-
# MEDIA_SERIALIZER.call(media)
|
35
|
-
MEDIA_SERIALIZER = lambda do |media, *|
|
36
|
-
{
|
37
|
-
type: media.kind, # i.e. 'image' / 'video' / 'audio' / ...
|
38
|
-
source: {
|
39
|
-
type: 'base64',
|
40
|
-
media_type: media.type, # i.e. 'image/jpeg' / 'video/ogg' / 'audio/mpeg' / ...
|
41
|
-
data: media.data,
|
42
|
-
},
|
43
|
-
}
|
44
|
-
end
|
45
|
-
|
46
30
|
# @return [Context]
|
47
31
|
CONTEXT = Context.build do |context|
|
48
|
-
context.serializers[:file] =
|
49
|
-
context.serializers[:url] =
|
32
|
+
context.serializers[:file] = MediaSerializer.method(:serialize)
|
33
|
+
context.serializers[:url] = MediaSerializer.method(:serialize)
|
34
|
+
|
35
|
+
context.serializers[:choice] = ChoiceSerializer.method(:serialize)
|
36
|
+
context.deserializers[:choice] = ChoiceSerializer.method(:deserialize)
|
37
|
+
|
38
|
+
context.serializers[:tool_call] = ToolCallSerializer.method(:serialize)
|
39
|
+
context.deserializers[:tool_call] = ToolCallSerializer.method(:deserialize)
|
40
|
+
|
41
|
+
context.serializers[:function] = FunctionSerializer.method(:serialize)
|
42
|
+
context.deserializers[:function] = FunctionSerializer.method(:deserialize)
|
43
|
+
|
44
|
+
context.deserializers[:content] = ContentSerializer.method(:deserialize)
|
45
|
+
context.deserializers[:payload] = PayloadSerializer.method(:deserialize)
|
50
46
|
end
|
51
47
|
|
52
48
|
# @return [Hash]
|
@@ -63,14 +59,16 @@ module OmniAI
|
|
63
59
|
|
64
60
|
# @return [Array<Hash>]
|
65
61
|
def messages
|
66
|
-
messages = @prompt.messages.
|
67
|
-
messages.map { |message| message.serialize(context:
|
62
|
+
messages = @prompt.messages.reject(&:system?)
|
63
|
+
messages.map { |message| message.serialize(context:) }
|
68
64
|
end
|
69
65
|
|
70
66
|
# @return [String, nil]
|
71
67
|
def system
|
72
68
|
messages = @prompt.messages.filter(&:system?)
|
73
|
-
|
69
|
+
return if messages.empty?
|
70
|
+
|
71
|
+
messages.filter(&:text?).map(&:text).join("\n\n")
|
74
72
|
end
|
75
73
|
|
76
74
|
# @return [String]
|
@@ -78,6 +76,13 @@ module OmniAI
|
|
78
76
|
"/#{Client::VERSION}/messages"
|
79
77
|
end
|
80
78
|
|
79
|
+
protected
|
80
|
+
|
81
|
+
# @return [Context]
|
82
|
+
def context
|
83
|
+
CONTEXT
|
84
|
+
end
|
85
|
+
|
81
86
|
private
|
82
87
|
|
83
88
|
# @return [Array<Hash>, nil]
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: omniai-anthropic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Sylvestre
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-08-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: event_stream_parser
|
@@ -63,8 +63,14 @@ files:
|
|
63
63
|
- README.md
|
64
64
|
- lib/omniai/anthropic.rb
|
65
65
|
- lib/omniai/anthropic/chat.rb
|
66
|
-
- lib/omniai/anthropic/chat/
|
67
|
-
- lib/omniai/anthropic/chat/
|
66
|
+
- lib/omniai/anthropic/chat/choice_serializer.rb
|
67
|
+
- lib/omniai/anthropic/chat/content_serializer.rb
|
68
|
+
- lib/omniai/anthropic/chat/function_serializer.rb
|
69
|
+
- lib/omniai/anthropic/chat/media_serializer.rb
|
70
|
+
- lib/omniai/anthropic/chat/payload_serializer.rb
|
71
|
+
- lib/omniai/anthropic/chat/stream.rb
|
72
|
+
- lib/omniai/anthropic/chat/text_serializer.rb
|
73
|
+
- lib/omniai/anthropic/chat/tool_call_serializer.rb
|
68
74
|
- lib/omniai/anthropic/client.rb
|
69
75
|
- lib/omniai/anthropic/config.rb
|
70
76
|
- lib/omniai/anthropic/version.rb
|
@@ -1,29 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module OmniAI
|
4
|
-
module Anthropic
|
5
|
-
class Chat
|
6
|
-
module Response
|
7
|
-
# A completion returned by the API.
|
8
|
-
class Completion < OmniAI::Chat::Response::Completion
|
9
|
-
# @return [Array<OmniAI::Chat::Response::MessageChoice>]
|
10
|
-
def choices
|
11
|
-
@choices ||= begin
|
12
|
-
role = @data['role']
|
13
|
-
|
14
|
-
@data['content'].map do |data, index|
|
15
|
-
OmniAI::Chat::Response::MessageChoice.new(data: {
|
16
|
-
'index' => index,
|
17
|
-
'message' => {
|
18
|
-
'role' => role,
|
19
|
-
'content' => data['text'],
|
20
|
-
},
|
21
|
-
})
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
@@ -1,111 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module OmniAI
|
4
|
-
module Anthropic
|
5
|
-
class Chat
|
6
|
-
module Response
|
7
|
-
# A stream given when streaming.
|
8
|
-
class Stream < OmniAI::Chat::Response::Stream
|
9
|
-
module Type
|
10
|
-
PING = 'ping'
|
11
|
-
MESSAGE_START = 'message_start'
|
12
|
-
MESSAGE_STOP = 'message_stop'
|
13
|
-
MESSAGE_DELTA = 'message_delta'
|
14
|
-
CONTENT_BLOCK_START = 'content_block_start'
|
15
|
-
CONTENT_BLOCK_STOP = 'content_block_stop'
|
16
|
-
CONTENT_BLOCK_DELTA = 'content_block_delta'
|
17
|
-
end
|
18
|
-
|
19
|
-
# Process the stream into chunks by event.
|
20
|
-
class Builder
|
21
|
-
attr_reader :id, :model, :role, :content, :index
|
22
|
-
|
23
|
-
# @return [OmniAI::Chat::Chunk]
|
24
|
-
def chunk
|
25
|
-
OmniAI::Chat::Response::Chunk.new(data: {
|
26
|
-
'id' => @id,
|
27
|
-
'model' => @model,
|
28
|
-
'choices' => [{
|
29
|
-
'index' => @index,
|
30
|
-
'delta' => {
|
31
|
-
'role' => @role,
|
32
|
-
'content' => @content,
|
33
|
-
},
|
34
|
-
}],
|
35
|
-
})
|
36
|
-
end
|
37
|
-
|
38
|
-
# Handler for Type::MESSAGE_START
|
39
|
-
#
|
40
|
-
# @param data [Hash]
|
41
|
-
def message_start(data)
|
42
|
-
@id = data['id']
|
43
|
-
@model = data['model']
|
44
|
-
@role = data['role']
|
45
|
-
end
|
46
|
-
|
47
|
-
# Handler for Type::MESSAGE_STOP
|
48
|
-
#
|
49
|
-
# @param _data [Hash]
|
50
|
-
def message_stop(_data)
|
51
|
-
@id = nil
|
52
|
-
@model = nil
|
53
|
-
@role = nil
|
54
|
-
end
|
55
|
-
|
56
|
-
# Handler for Type::CONTENT_BLOCK_START
|
57
|
-
#
|
58
|
-
# @param data [Hash]
|
59
|
-
def content_block_start(data)
|
60
|
-
@index = data['index']
|
61
|
-
end
|
62
|
-
|
63
|
-
# Handler for Type::CONTENT_BLOCK_STOP
|
64
|
-
#
|
65
|
-
# @param _data [Hash]
|
66
|
-
def content_block_stop(_data)
|
67
|
-
@index = nil
|
68
|
-
end
|
69
|
-
|
70
|
-
# Handler for Type::CONTENT_BLOCK_DELTA
|
71
|
-
#
|
72
|
-
# @param data [Hash]
|
73
|
-
def content_block_delta(data)
|
74
|
-
return unless data['delta']['type'].eql?('text_delta')
|
75
|
-
|
76
|
-
@content = data['delta']['text']
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
# @yield [OmniAI::Chat::Chunk]
|
81
|
-
def stream!(&block)
|
82
|
-
builder = Builder.new
|
83
|
-
|
84
|
-
@response.body.each do |chunk|
|
85
|
-
@parser.feed(chunk) do |type, data|
|
86
|
-
process(type:, data: JSON.parse(data), builder:, &block)
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
private
|
92
|
-
|
93
|
-
# @param type [String]
|
94
|
-
# @param data [Hash]
|
95
|
-
# @param builder [Builder]
|
96
|
-
def process(type:, data:, builder:, &)
|
97
|
-
case type
|
98
|
-
when Type::MESSAGE_START then builder.message_start(data)
|
99
|
-
when Type::CONTENT_BLOCK_START then builder.content_block_start(data)
|
100
|
-
when Type::CONTENT_BLOCK_STOP then builder.content_block_stop(data)
|
101
|
-
when Type::MESSAGE_STOP then builder.message_stop(data)
|
102
|
-
when Type::CONTENT_BLOCK_DELTA
|
103
|
-
builder.content_block_delta(data)
|
104
|
-
yield(builder.chunk)
|
105
|
-
end
|
106
|
-
end
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|