omniai-llama 0.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 25c0a8a9ba448d407d0f8300d377d0e3a10a0aa1b18343540a0b32f3eb3baeb5
4
+ data.tar.gz: 67d4173043933e6314df901969eae71b0ab1137dfd8da4365377824eeed167dd
5
+ SHA512:
6
+ metadata.gz: 2e43f7b12d1dbcbe2b02c9a7bc206ee0c1980c231843cb7fc292774548fed69ebb492a17aa8d451374a106bcc7d24f9ec0af27cce34004459493174b74854243
7
+ data.tar.gz: b991b560116560bff895708f89cc28d01c20a1f477ab69625fb910249bdc7b853eb91f23666b8d1fedb7caaac5f715bc01c3cfa749ee91a0d4929d9e9ac1f3ec
data/Gemfile ADDED
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec
6
+
7
+ gem "rake"
8
+ gem "redcarpet"
9
+ gem "rspec"
10
+ gem "rspec_junit_formatter"
11
+ gem "rubocop"
12
+ gem "rubocop-basic"
13
+ gem "rubocop-rake"
14
+ gem "rubocop-rspec"
15
+ gem "simplecov"
16
+ gem "webmock"
17
+ gem "yard"
data/README.md ADDED
@@ -0,0 +1,432 @@
1
+ # OmniAI::Llama
2
+
3
+ [![LICENSE](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/ksylvest/omniai-llama/blob/main/LICENSE)
4
+ [![RubyGems](https://img.shields.io/gem/v/omniai-llama)](https://rubygems.org/gems/omniai-llama)
5
+ [![GitHub](https://img.shields.io/badge/github-repo-blue.svg)](https://github.com/ksylvest/omniai-llama)
6
+ [![Yard](https://img.shields.io/badge/docs-site-blue.svg)](https://omniai-llama.ksylvest.com)
7
+ [![CircleCI](https://img.shields.io/circleci/build/github/ksylvest/omniai-llama)](https://circleci.com/gh/ksylvest/omniai-llama)
8
+
9
+ A implementation of the [OmniAI](https://github.com/ksylvest/omniai) for api.llama.com.
10
+
11
+ ## Installation
12
+
13
+ ```sh
14
+ gem install omniai-llama
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ ### Client
20
+
21
+ A client is setup as follows if `ENV['LLAMA_API_KEY']` exists:
22
+
23
+ ```ruby
24
+ client = OmniAI::Llama::Client.new
25
+ ```
26
+
27
+ A client may also be passed the following options:
28
+
29
+ - `api_key` (required - default is `ENV['LLAMA_API_KEY']`)
30
+
31
+ ### Configuration
32
+
33
+ Global configuration is supported for the following options:
34
+
35
+ ```ruby
36
+ OmniAI::Llama.configure do |config|
37
+ config.api_key = 'sk-...' # default: ENV['LLAMA_API_KEY']
38
+ end
39
+ ```
40
+
41
+ ### Chat
42
+
43
+ A chat completion is generated by passing in a simple text prompt:
44
+
45
+ ```ruby
46
+ completion = client.chat('Tell me a joke!')
47
+ completion.content # 'Why did the chicken cross the road? To get to the other side.'
48
+ ```
49
+
50
+ A chat completion may also be generated by using a prompt builder:
51
+
52
+ ```ruby
53
+ completion = client.chat do |prompt|
54
+ prompt.system('Your are an expert in geography.')
55
+ prompt.user('What is the capital of Canada?')
56
+ end
57
+ completion.content # 'The capital of Canada is Ottawa.'
58
+ ```
59
+
60
+ #### Model
61
+
62
+ `model` takes an optional string (default is `gpt-4o`):
63
+
64
+ ```ruby
65
+ completion = client.chat('How fast is a cheetah?', model: OmniAI::Llama::Chat::Model::GPT_3_5_TURBO)
66
+ completion.content # 'A cheetah can reach speeds over 100 km/h.'
67
+ ```
68
+
69
+ [OpenAI API Reference `model`](https://platform.openai.com/docs/api-reference/chat/create#chat-create-model)
70
+
71
+ #### Temperature
72
+
73
+ `temperature` takes an optional float between `0.0` and `2.0` (defaults is `0.7`):
74
+
75
+ ```ruby
76
+ completion = client.chat('Pick a number between 1 and 5', temperature: 2.0)
77
+ completion.content # '3'
78
+ ```
79
+
80
+ [OpenAI API Reference `temperature`](https://platform.openai.com/docs/api-reference/chat/create#chat-create-temperature)
81
+
82
+ #### Stream
83
+
84
+ `stream` takes an optional a proc to stream responses in real-time chunks instead of waiting for a complete response:
85
+
86
+ ```ruby
87
+ stream = proc do |chunk|
88
+ print(chunk.content) # 'Better', 'three', 'hours', ...
89
+ end
90
+ client.chat('Be poetic.', stream:)
91
+ ```
92
+
93
+ [OpenAI API Reference `stream`](https://platform.openai.com/docs/api-reference/chat/create#chat-create-stream)
94
+
95
+ #### Format
96
+
97
+ `format` takes an optional symbol (`:json`) and that setes the `response_format` to `json_object`:
98
+
99
+ ```ruby
100
+ completion = client.chat(format: :json) do |prompt|
101
+ prompt.system(OmniAI::Chat::JSON_PROMPT)
102
+ prompt.user('What is the name of the drummer for the Beatles?')
103
+ end
104
+ JSON.parse(completion.content) # { "name": "Ringo" }
105
+ ```
106
+
107
+ [OpenAI API Reference `response_format`](https://platform.openai.com/docs/api-reference/chat/create#chat-create-stream)
108
+
109
+ > When using JSON mode, you must also instruct the model to produce JSON yourself via a system or user message.
110
+
111
+ ### Transcribe
112
+
113
+ A transcription is generated by passing in a path to a file:
114
+
115
+ ```ruby
116
+ transcription = client.transcribe(file.path)
117
+ transcription.text # '...'
118
+ ```
119
+
120
+ #### Prompt
121
+
122
+ `prompt` is optional and can provide additional context for transcribing:
123
+
124
+ ```ruby
125
+ transcription = client.transcribe(file.path, prompt: '')
126
+ transcription.text # '...'
127
+ ```
128
+
129
+ [OpenAI API Reference `prompt`](https://platform.openai.com/docs/api-reference/audio/createTranscription#audio-createtranscription-prompt)
130
+
131
+ #### Format
132
+
133
+ `format` is optional and supports `json`, `text`, `srt` or `vtt`:
134
+
135
+ ```ruby
136
+ transcription = client.transcribe(file.path, format: OmniAI::Transcribe::Format::TEXT)
137
+ transcription.text # '...'
138
+ ```
139
+
140
+ [OpenAI API Reference `response_format`](https://platform.openai.com/docs/api-reference/audio/createTranscription#audio-createtranscription-response_format)
141
+
142
+ #### Language
143
+
144
+ `language` is optional and may improve accuracy and latency:
145
+
146
+ ```ruby
147
+ transcription = client.transcribe(file.path, language: OmniAI::Transcribe::Language::SPANISH)
148
+ transcription.text
149
+ ```
150
+
151
+ [OpenAI API Reference `language`](https://platform.openai.com/docs/api-reference/audio/createTranscription#audio-createtranscription-language)
152
+
153
+ #### Temperature
154
+
155
+ `temperature` is optional and must be between 0.0 (more deterministic) and 1.0 (less deterministic):
156
+
157
+ ```ruby
158
+ transcription = client.transcribe(file.path, temperature: 0.2)
159
+ transcription.text
160
+ ```
161
+
162
+ [OpenAI API Reference `temperature`](https://platform.openai.com/docs/api-reference/audio/createTranscription#audio-createtranscription-temperature)
163
+
164
+ ### Speak
165
+
166
+ Speech can be generated by passing text with a block:
167
+
168
+ ```ruby
169
+ File.open('example.ogg', 'wb') do |file|
170
+ client.speak('How can a clam cram in a clean cream can?') do |chunk|
171
+ file << chunk
172
+ end
173
+ end
174
+ ```
175
+
176
+ If a block is not provided then a tempfile is returned:
177
+
178
+ ```ruby
179
+ tempfile = client.speak('Can you can a can as a canner can can a can?')
180
+ tempfile.close
181
+ tempfile.unlink
182
+ ```
183
+
184
+ #### Voice
185
+
186
+ `voice` is optional and must be one of the supported voices:
187
+
188
+ ```ruby
189
+ client.speak('She sells seashells by the seashore.', voice: OmniAI::Llama::Speak::Voice::SHIMMER)
190
+ ```
191
+
192
+ [OpenAI API Reference `voice`](https://platform.openai.com/docs/api-reference/audio/createSpeech#audio-createspeech-voice)
193
+
194
+ #### Model
195
+
196
+ `model` is optional and must be either `tts-1` or `tts-1-hd` (default):
197
+
198
+ ```ruby
199
+ client.speak('I saw a kitten eating chicken in the kitchen.', format: OmniAI::Llama::Speak::Model::TTS_1)
200
+ ```
201
+
202
+ [OpenAI API Refernce `model`](https://platform.openai.com/docs/api-reference/audio/createSpeech#audio-createspeech-model)
203
+
204
+ #### Speed
205
+
206
+ `speed` is optional and must be between 0.25 and 0.40:
207
+
208
+ ```ruby
209
+ client.speak('How much wood would a woodchuck chuck if a woodchuck could chuck wood?', speed: 4.0)
210
+ ```
211
+
212
+ [OmniAI API Reference `speed`](https://platform.openai.com/docs/api-reference/audio/createSpeech#audio-createspeech-speed)
213
+
214
+ #### Format
215
+
216
+ `format` is optional and supports `MP3` (default), `OPUS`, `AAC`, `FLAC`, `WAV` or `PCM`:
217
+
218
+ ```ruby
219
+ client.speak('A pessemistic pest exists amidst us.', format: OmniAI::Llama::Speak::Format::FLAC)
220
+ ```
221
+
222
+ [OpenAI API Reference `format`](https://platform.openai.com/docs/api-reference/audio/createSpeech#audio-createspeech-response_format)
223
+
224
+ ## Files
225
+
226
+ ### Finding an File
227
+
228
+ ```ruby
229
+ client.files.find(id: 'file_...')
230
+ ```
231
+
232
+ ### Listing all Files
233
+
234
+ ```ruby
235
+ client.files.all
236
+ ```
237
+
238
+ ### Uploading a File
239
+
240
+ #### Using a File
241
+
242
+ ```ruby
243
+ file = client.files.build(io: File.open('demo.pdf', 'wb'))
244
+ file.save!
245
+ ```
246
+
247
+ #### Using a Path
248
+
249
+ ```ruby
250
+ file = client.files.build(io: 'demo.pdf'))
251
+ file.save!
252
+ ```
253
+
254
+ ### Downloading a File
255
+
256
+ ```ruby
257
+ file = client.files.find(id: 'file_...')
258
+ File.open('...', 'wb') do |file|
259
+ file.content do |chunk|
260
+ file << chunk
261
+ end
262
+ end
263
+ ```
264
+
265
+ ### Destroying a File
266
+
267
+ ```ruby
268
+ client.files.destroy!('file_...')
269
+ ```
270
+
271
+ ## Assistants
272
+
273
+ ### Finding an Assistant
274
+
275
+ ```ruby
276
+ client.assistants.find(id: 'asst_...')
277
+ ```
278
+
279
+ ### Listing all Assistants
280
+
281
+ ```ruby
282
+ client.assistants.all
283
+ ```
284
+
285
+ ### Creating an Assistant
286
+
287
+ ```ruby
288
+ assistant = client.assistants.build
289
+ assistant.name = 'Ringo'
290
+ assistant.model = OmniAI::Llama::Chat::Model::GPT_4
291
+ assistant.description = 'The drummer for the Beatles.'
292
+ assistant.save!
293
+ ```
294
+
295
+ ### Updating an Assistant
296
+
297
+ ```ruby
298
+ assistant = client.assistants.find(id: 'asst_...')
299
+ assistant.name = 'George'
300
+ assistant.model = OmniAI::Llama::Chat::Model::GPT_4
301
+ assistant.description = 'A guitarist for the Beatles.'
302
+ assistant.save!
303
+ ```
304
+
305
+ ### Destroying an Assistant
306
+
307
+ ```ruby
308
+ client.assistants.destroy!('asst_...')
309
+ ```
310
+
311
+ ## Threads
312
+
313
+ ### Finding a Thread
314
+
315
+ ```ruby
316
+ client.threads.find(id: 'thread_...')
317
+ ```
318
+
319
+ ### Creating a Thread
320
+
321
+ ```ruby
322
+ thread = client.threads.build
323
+ thread.metadata = { user: 'Ringo' }
324
+ thread.save!
325
+ ```
326
+
327
+ ### Updating a Thread
328
+
329
+ ```ruby
330
+ thread = client.threads.find(id: 'thread_...')
331
+ thread.metadata = { user: 'Ringo' }
332
+ thread.save!
333
+ ```
334
+
335
+ ### Destroying a Threads
336
+
337
+ ```ruby
338
+ client.threads.destroy!('thread_...')
339
+ ```
340
+
341
+ ### Messages
342
+
343
+ #### Finding a Message
344
+
345
+ ```ruby
346
+ thread = client.threads.find(id: 'thread_...')
347
+ message = thread.messages.find(id: 'msg_...')
348
+ message.save!
349
+ ```
350
+
351
+ #### Listing all Messages
352
+
353
+ ```ruby
354
+ thread = client.threads.find(id: 'thread_...')
355
+ thread.messages.all
356
+ ```
357
+
358
+ #### Creating a Message
359
+
360
+ ```ruby
361
+ thread = client.threads.find(id: 'thread_...')
362
+ message = thread.messages.build(role: 'user', content: 'Hello?')
363
+ message.save!
364
+ ```
365
+
366
+ #### Updating a Message
367
+
368
+ ```ruby
369
+ thread = client.threads.find(id: 'thread_...')
370
+ message = thread.messages.build(role: 'user', content: 'Hello?')
371
+ message.save!
372
+ ```
373
+
374
+ ### Runs
375
+
376
+ #### Finding a Run
377
+
378
+ ```ruby
379
+ thread = client.threads.find(id: 'thread_...')
380
+ run = thread.runs.find(id: 'run_...')
381
+ run.save!
382
+ ```
383
+
384
+ #### Listing all Runs
385
+
386
+ ```ruby
387
+ thread = client.threads.find(id: 'thread_...')
388
+ thread.runs.all
389
+ ```
390
+
391
+ #### Creating a Run
392
+
393
+ ```ruby
394
+ run = client.runs.find(id: 'thread_...')
395
+ run = thread.runs.build
396
+ run.metadata = { user: 'Ringo' }
397
+ run.save!
398
+ ```
399
+
400
+ #### Updating a Run
401
+
402
+ ```ruby
403
+ thread = client.threads.find(id: 'thread_...')
404
+ run = thread.messages.find(id: 'run_...')
405
+ run.metadata = { user: 'Ringo' }
406
+ run.save!
407
+ ```
408
+
409
+ #### Polling a Run
410
+
411
+ ```ruby
412
+ run.terminated? # false
413
+ run.poll!
414
+ run.terminated? # true
415
+ run.status # 'cancelled' / 'failed' / 'completed' / 'expired'
416
+ ```
417
+
418
+ #### Cancelling a Run
419
+
420
+ ```ruby
421
+ thread = client.threads.find(id: 'thread_...')
422
+ run = thread.runs.cancel!(id: 'run_...')
423
+ ```
424
+
425
+ ### Embed
426
+
427
+ Text can be converted into a vector embedding for similarity comparison usage via:
428
+
429
+ ```ruby
430
+ response = client.embed('The quick brown fox jumps over a lazy dog.')
431
+ response.embedding # [0.0, ...]
432
+ ```
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ module Llama
5
+ class Chat
6
+ # Overrides choice serialize / deserialize for the following payload:
7
+ #
8
+ # {
9
+ # content: {
10
+ # type: "text",
11
+ # text: "Hello!",
12
+ # },
13
+ # role: "assistant",
14
+ # stop_reason: "stop",
15
+ # tool_calls: [],
16
+ # }
17
+ module ChoiceSerializer
18
+ # @param data [Hash]
19
+ # @param context [OmniAI::Context]
20
+ #
21
+ # @return [OmniAI::Chat::Response]
22
+ def self.deserialize(data, context:)
23
+ message = OmniAI::Chat::Message.deserialize(data, context:)
24
+ OmniAI::Chat::Choice.new(message:)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ module Llama
5
+ class Chat
6
+ # Overrides choice serialize / deserialize for the following payload:
7
+ #
8
+ # {
9
+ # content: {
10
+ # type: "text",
11
+ # text: "Hello!",
12
+ # },
13
+ # role: "assistant",
14
+ # stop_reason: "stop",
15
+ # tool_calls: [],
16
+ # }
17
+ module MessageSerializer
18
+ # @param data [Hash]
19
+ # @param context [OmniAI::Context]
20
+ #
21
+ # @return [OmniAI::Chat::Message]
22
+ def self.deserialize(data, context:)
23
+ role = data["role"]
24
+ content = OmniAI::Chat::Content.deserialize(data["content"], context:)
25
+
26
+ OmniAI::Chat::Message.new(content:, role:)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ module Llama
5
+ class Chat
6
+ # Overrides response serialize / deserialize for the following payload:
7
+ #
8
+ # {
9
+ # completion_message: {
10
+ # content: {
11
+ # type: "text",
12
+ # text: "Hello!",
13
+ # },
14
+ # role: "assistant",
15
+ # stop_reason: "stop",
16
+ # tool_calls: [],
17
+ # },
18
+ # metrics: [
19
+ # {
20
+ # metric: "num_completion_tokens",
21
+ # value: 25,
22
+ # unit: "tokens",
23
+ # },
24
+ # {
25
+ # metric: "num_prompt_tokens",
26
+ # value: 25,
27
+ # unit: "tokens",
28
+ # },
29
+ # {
30
+ # metric: "num_total_tokens",
31
+ # value: 50,
32
+ # unit: "tokens",
33
+ # },
34
+ # ],
35
+ # }
36
+ module ResponseSerializer
37
+ # @param data [Hash]
38
+ # @param context [OmniAI::Context]
39
+ #
40
+ # @return [OmniAI::Chat::Response]
41
+ def self.deserialize(data, context:)
42
+ usage = OmniAI::Chat::Usage.deserialize(data["metrics"], context:) if data["metrics"]
43
+ choice = OmniAI::Chat::Choice.deserialize(data["completion_message"], context:)
44
+
45
+ OmniAI::Chat::Response.new(data:, choices: [choice], usage:)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ module Llama
5
+ class Chat
6
+ # Overrides response serialize / deserialize for the following payload:
7
+ #
8
+ # [
9
+ # {
10
+ # metric: "num_completion_tokens",
11
+ # value: 25,
12
+ # unit: "tokens",
13
+ # },
14
+ # {
15
+ # metric: "num_prompt_tokens",
16
+ # value: 25,
17
+ # unit: "tokens",
18
+ # },
19
+ # {
20
+ # metric: "num_total_tokens",
21
+ # value: 50,
22
+ # unit: "tokens",
23
+ # },
24
+ # ]
25
+ module UsageSerializer
26
+ module Metric
27
+ NUM_PROMPT_TOKENS = "num_prompt_tokens"
28
+ NUM_COMPLETION_TOKENS = "num_completion_tokens"
29
+ NUM_TOTAL_TOKENS = "num_total_tokens"
30
+ end
31
+
32
+ # @param data [Hash]
33
+ #
34
+ # @return [OmniAI::Chat::Response]
35
+ def self.deserialize(data, *)
36
+ prompt = data.find { |metric| metric["metric"] == Metric::NUM_PROMPT_TOKENS }
37
+ completion = data.find { |metric| metric["metric"] == Metric::NUM_COMPLETION_TOKENS }
38
+ total = data.find { |metric| metric["metric"] == Metric::NUM_TOTAL_TOKENS }
39
+
40
+ input_tokens = prompt ? prompt["value"] : 0
41
+ output_tokens = completion ? completion["value"] : 0
42
+ total_tokens = total ? total["value"] : 0
43
+
44
+ OmniAI::Chat::Usage.new(input_tokens:, output_tokens:, total_tokens:)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ module Llama
5
+ # An OpenAI chat implementation.
6
+ #
7
+ # Usage:
8
+ #
9
+ # completion = OmniAI::Llama::Chat.process!(client: client) do |prompt|
10
+ # prompt.system('You are an expert in the field of AI.')
11
+ # prompt.user('What are the biggest risks of AI?')
12
+ # end
13
+ # completion.choice.message.content # '...'
14
+ class Chat < OmniAI::Chat
15
+ JSON_RESPONSE_FORMAT = { type: "json_object" }.freeze
16
+ DEFAULT_STREAM_OPTIONS = { include_usage: ENV.fetch("OMNIAI_STREAM_USAGE", "on").eql?("on") }.freeze
17
+
18
+ module Model
19
+ LLAMA_4_SCOUT_17B_16E_INSTRUCT_FP8 = "Llama-4-Scout-17B-16E-Instruct-FP8"
20
+ LLAMA_4_MAVERICK_17B_128E_INSTRUCT_FP_8 = "Llama-4-Maverick-17B-128E-Instruct-FP8"
21
+ LLAMA_3_3_70B_INSTRUCT = "Llama-3.3-70B-Instruct"
22
+ LLAMA_3_3_8B_INSTRUCT = "Llama-3.3-8B-Instruct"
23
+ LLAMA_4_SCOUT = LLAMA_4_SCOUT_17B_16E_INSTRUCT_FP8
24
+ LLAMA_4_MAVERICK = LLAMA_4_MAVERICK_17B_128E_INSTRUCT_FP_8
25
+ end
26
+
27
+ DEFAULT_MODEL = Model::LLAMA_4_SCOUT
28
+
29
+ # @return [Context]
30
+ CONTEXT = Context.build do |context|
31
+ context.deserializers[:response] = ResponseSerializer.method(:deserialize)
32
+ context.deserializers[:choice] = ChoiceSerializer.method(:deserialize)
33
+ context.deserializers[:message] = MessageSerializer.method(:deserialize)
34
+ context.deserializers[:usage] = UsageSerializer.method(:deserialize)
35
+ end
36
+
37
+ protected
38
+
39
+ # @return [Context]
40
+ def context
41
+ CONTEXT
42
+ end
43
+
44
+ # @return [Hash]
45
+ def payload
46
+ OmniAI::Llama.config.chat_options.merge({
47
+ messages: @prompt.serialize,
48
+ model: @model,
49
+ response_format: (JSON_RESPONSE_FORMAT if @format.eql?(:json)),
50
+ stream: stream? || nil,
51
+ stream_options: (DEFAULT_STREAM_OPTIONS if stream?),
52
+ temperature: @temperature,
53
+ tools: (@tools.map(&:serialize) if @tools&.any?),
54
+ }).compact
55
+ end
56
+
57
+ # @return [String]
58
+ def path
59
+ "/#{OmniAI::Llama::Client::VERSION}/chat/completions"
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ module Llama
5
+ # A Llama client implementation. Usage:
6
+ #
7
+ # w/ `api_key``:
8
+ # client = OmniAI::Llama::Client.new(api_key: '...')
9
+ #
10
+ # w/ ENV['LLAMA_API_KEY']:
11
+ #
12
+ # ENV['LLAMA_API_KEY'] = '...'
13
+ # client = OmniAI::Llama::Client.new
14
+ #
15
+ # w/ config:
16
+ #
17
+ # OmniAI::Llama.configure do |config|
18
+ # config.api_key = '...'
19
+ # end
20
+ #
21
+ # client = OmniAI::Llama::Client.new
22
+ class Client < OmniAI::Client
23
+ VERSION = "v1"
24
+
25
+ # @param api_key [String, nil] optional - defaults to `OmniAI::Llama.config.api_key`
26
+ # @param host [String] optional - defaults to `OmniAI::Llama.config.host`
27
+ # @param logger [Logger, nil] optional - defaults to `OmniAI::Llama.config.logger`
28
+ # @param timeout [Integer, nil] optional - defaults to `OmniAI::Llama.config.timeout`
29
+ def initialize(
30
+ api_key: OmniAI::Llama.config.api_key,
31
+ host: OmniAI::Llama.config.host,
32
+ logger: OmniAI::Llama.config.logger,
33
+ timeout: OmniAI::Llama.config.timeout
34
+ )
35
+ raise(ArgumentError, %(ENV['LLAMA_API_KEY'] must be defined or `api_key` must be passed)) if api_key.nil?
36
+
37
+ super
38
+ end
39
+
40
+ # @return [HTTP::Client]
41
+ def connection
42
+ @connection ||= begin
43
+ http = super
44
+ http = http.auth("Bearer #{@api_key}") if @api_key
45
+ http
46
+ end
47
+ end
48
+
49
+ # @raise [OmniAI::Error]
50
+ #
51
+ # @param messages [String] optional
52
+ # @param model [String] optional
53
+ # @param format [Symbol] optional :text or :json
54
+ # @param temperature [Float, nil] optional
55
+ # @param stream [Proc, nil] optional
56
+ # @param tools [Array<OmniAI::Tool>, nil] optional
57
+ #
58
+ # @yield [prompt]
59
+ # @yieldparam prompt [OmniAI::Chat::Prompt]
60
+ #
61
+ # @return [OmniAI::Chat::Completion]
62
+ def chat(messages = nil, model: Chat::DEFAULT_MODEL, temperature: nil, format: nil, stream: nil, tools: nil, &)
63
+ Chat.process!(messages, model:, temperature:, format:, stream:, tools:, client: self, &)
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ module Llama
5
+ # Configuration for OpenAI.
6
+ class Config < OmniAI::Config
7
+ DEFAULT_HOST = "https://api.llama.com"
8
+
9
+ # @param api_key [String, nil] optional - defaults to `ENV['LLAMA_API_KEY']`
10
+ # @param host [String, nil] optional - defaults to ENV['LLAMA_HOST'] w/ fallback to `DEFAULT_HOST`
11
+ # @param logger [Logger, nil] optional
12
+ # @param timeout [Integer, Hash, nil] optional
13
+ def initialize(
14
+ api_key: ENV.fetch("LLAMA_API_KEY", nil),
15
+ host: ENV.fetch("LLAMA_HOST", DEFAULT_HOST),
16
+ logger: nil,
17
+ timeout: nil
18
+ )
19
+ super
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ module Llama
5
+ VERSION = "0.0.1"
6
+ end
7
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "event_stream_parser"
4
+ require "omniai"
5
+ require "zeitwerk"
6
+
7
+ loader = Zeitwerk::Loader.for_gem
8
+ loader.push_dir(__dir__, namespace: OmniAI)
9
+ loader.setup
10
+
11
+ module OmniAI
12
+ # A namespace for everything Llama.
13
+ module Llama
14
+ # @return [OmniAI::Llama::Config]
15
+ def self.config
16
+ @config ||= Config.new
17
+ end
18
+
19
+ # @yield [OmniAI::Llama::Config]
20
+ def self.configure
21
+ yield config
22
+ end
23
+ end
24
+ end
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: omniai-llama
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Kevin Sylvestre
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 2025-05-01 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: event_stream_parser
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: omniai
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '2.2'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '2.2'
40
+ - !ruby/object:Gem::Dependency
41
+ name: zeitwerk
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ description: An implementation of OmniAI for OpenAI
55
+ email:
56
+ - kevin@ksylvest.com
57
+ executables: []
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - Gemfile
62
+ - README.md
63
+ - lib/omniai/llama.rb
64
+ - lib/omniai/llama/chat.rb
65
+ - lib/omniai/llama/chat/choice_serializer.rb
66
+ - lib/omniai/llama/chat/message_serializer.rb
67
+ - lib/omniai/llama/chat/response_serializer.rb
68
+ - lib/omniai/llama/chat/usage_serializer.rb
69
+ - lib/omniai/llama/client.rb
70
+ - lib/omniai/llama/config.rb
71
+ - lib/omniai/llama/version.rb
72
+ homepage: https://github.com/ksylvest/omniai-llama
73
+ licenses:
74
+ - MIT
75
+ metadata:
76
+ homepage_uri: https://github.com/ksylvest/omniai-llama
77
+ changelog_uri: https://github.com/ksylvest/omniai-llama/releases
78
+ rubygems_mfa_required: 'true'
79
+ rdoc_options: []
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: 3.2.0
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ requirements: []
93
+ rubygems_version: 3.6.6
94
+ specification_version: 4
95
+ summary: A generalized framework for interacting with OpenAI
96
+ test_files: []