omniai-deepseek 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/Gemfile +18 -0
- data/README.md +111 -0
- data/lib/omniai/deepseek/chat.rb +44 -0
- data/lib/omniai/deepseek/client.rb +63 -0
- data/lib/omniai/deepseek/config.rb +23 -0
- data/lib/omniai/deepseek/version.rb +7 -0
- data/lib/omniai/deepseek.rb +25 -0
- metadata +92 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2b01ec27c812ac1fd9d44619b8bf622b3e2f9f2de08e18ae5b2ecb77f8304146
|
4
|
+
data.tar.gz: 5c64510f011221a229130b58e486e771d10a51aab36f8b80616f38cf1ce4dfa9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a8c786ce882c8ed8f96227b05cd5def3315f595f73a873f5890224056990a79167ce401bad34d383f25c29953495bbaceb5a06dde137b88c95acaa665a0a9b02
|
7
|
+
data.tar.gz: 894147e0f0b1e0c8aa3c8ad6fb2faece3a9560c04ffa11783182f5b9ff8457eccb37222b6c97c382fe2110f20ed47799e433fc8c863306c7c99c62b9152269dd
|
data/Gemfile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gemspec
|
6
|
+
|
7
|
+
gem "irb"
|
8
|
+
gem "logger"
|
9
|
+
gem "rake"
|
10
|
+
gem "rspec"
|
11
|
+
gem "rspec_junit_formatter"
|
12
|
+
gem "rubocop"
|
13
|
+
gem "rubocop-basic"
|
14
|
+
gem "rubocop-rake"
|
15
|
+
gem "rubocop-rspec"
|
16
|
+
gem "simplecov"
|
17
|
+
gem "webmock"
|
18
|
+
gem "yard"
|
data/README.md
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
# OmniAI::DeepSeek
|
2
|
+
|
3
|
+
[](https://github.com/ksylvest/omniai-deepseek/blob/main/LICENSE)
|
4
|
+
[](https://rubygems.org/gems/omniai-deepseek)
|
5
|
+
[](https://github.com/ksylvest/omniai-deepseek)
|
6
|
+
[](https://omniai-deepseek.ksylvest.com)
|
7
|
+
[](https://circleci.com/gh/ksylvest/omniai-deepseek)
|
8
|
+
|
9
|
+
An DeepSeek implementation of the [OmniAI](https://github.com/ksylvest/omniai) interface for [deepseek](https://www.deepseek.com/).
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
```sh
|
14
|
+
gem install omniai-deepseek
|
15
|
+
```
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
### Client
|
20
|
+
|
21
|
+
A client is setup as follows if `ENV['DEEPSEEK_API_KEY']` exists:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
client = OmniAI::DeepSeek::Client.new
|
25
|
+
```
|
26
|
+
|
27
|
+
A client may also be passed the following options:
|
28
|
+
|
29
|
+
- `api_key` (required - default is `ENV['DEEPSEEK_API_KEY']`)
|
30
|
+
- `host` (optional)
|
31
|
+
|
32
|
+
### Configuration
|
33
|
+
|
34
|
+
Global configuration is supported for the following options:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
OmniAI::DeepSeek.configure do |config|
|
38
|
+
config.api_key = 'sk-...' # default: ENV['DEEPSEEK_API_KEY']
|
39
|
+
config.host = '...' # default: 'https://api.deepseek.com'
|
40
|
+
end
|
41
|
+
```
|
42
|
+
|
43
|
+
### Chat
|
44
|
+
|
45
|
+
A chat completion is generated by passing in a simple text prompt:
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
completion = client.chat('Tell me a joke!')
|
49
|
+
completion.content # 'Why did the chicken cross the road? To get to the other side.'
|
50
|
+
```
|
51
|
+
|
52
|
+
A chat completion may also be generated by using a prompt builder:
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
completion = client.chat do |prompt|
|
56
|
+
prompt.system('Your are an expert in geography.')
|
57
|
+
prompt.user('What is the capital of Canada?')
|
58
|
+
end
|
59
|
+
completion.content # 'The capital of Canada is Ottawa.'
|
60
|
+
```
|
61
|
+
|
62
|
+
#### Model
|
63
|
+
|
64
|
+
`model` takes an optional string (default is `gpt-4o`):
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
completion = client.chat('How fast is a cheetah?', model: OmniAI::DeepSeek::Chat::Model::REASONER)
|
68
|
+
completion.content # 'A cheetah can reach speeds over 100 km/h.'
|
69
|
+
```
|
70
|
+
|
71
|
+
[DeepSeek API Reference `model`](https://api-docs.deepseek.com/quick_start/pricing)
|
72
|
+
|
73
|
+
#### Temperature
|
74
|
+
|
75
|
+
`temperature` takes an optional float between `0.0` and `2.0` (defaults is `0.7`):
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
completion = client.chat('Pick a number between 1 and 5', temperature: 2.0)
|
79
|
+
completion.content # '3'
|
80
|
+
```
|
81
|
+
|
82
|
+
[DeepSeek API Reference `temperature`](https://api-docs.deepseek.com/quick_start/parameter_settings)
|
83
|
+
|
84
|
+
#### Stream
|
85
|
+
|
86
|
+
`stream` takes an optional a proc to stream responses in real-time chunks instead of waiting for a complete response:
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
stream = proc do |chunk|
|
90
|
+
print(chunk.content) # 'Better', 'three', 'hours', ...
|
91
|
+
end
|
92
|
+
client.chat('Be poetic.', stream:)
|
93
|
+
```
|
94
|
+
|
95
|
+
[DeepSeek API Reference `stream`](https://platform.deepseek.com/docs/api-reference/chat/create#chat-create-stream)
|
96
|
+
|
97
|
+
#### Format
|
98
|
+
|
99
|
+
`format` takes an optional symbol (`:json`) and that setes the `response_format` to `json_object`:
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
completion = client.chat(format: :json) do |prompt|
|
103
|
+
prompt.system(OmniAI::Chat::JSON_PROMPT)
|
104
|
+
prompt.user('What is the name of the drummer for the Beatles?')
|
105
|
+
end
|
106
|
+
JSON.parse(completion.content) # { "name": "Ringo" }
|
107
|
+
```
|
108
|
+
|
109
|
+
[DeepSeek API Reference `response_format`](https://platform.deepseek.com/docs/api-reference/chat/create#chat-create-stream)
|
110
|
+
|
111
|
+
> When using JSON mode, you must also instruct the model to produce JSON yourself via a system or user message.
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OmniAI
|
4
|
+
module DeepSeek
|
5
|
+
# An DeepSeek chat implementation.
|
6
|
+
#
|
7
|
+
# Usage:
|
8
|
+
#
|
9
|
+
# completion = OmniAI::DeepSeek::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
|
+
|
17
|
+
module Model
|
18
|
+
CHAT = "deepseek-chat"
|
19
|
+
REASONER = "deepseek-reasoner"
|
20
|
+
end
|
21
|
+
|
22
|
+
DEFAULT_MODEL = Model::CHAT
|
23
|
+
|
24
|
+
protected
|
25
|
+
|
26
|
+
# @return [Hash]
|
27
|
+
def payload
|
28
|
+
OmniAI::DeepSeek.config.chat_options.merge({
|
29
|
+
messages: @prompt.serialize,
|
30
|
+
model: @model,
|
31
|
+
stream: @stream.nil? ? nil : !@stream.nil?,
|
32
|
+
temperature: @temperature,
|
33
|
+
response_format: (JSON_RESPONSE_FORMAT if @format.eql?(:json)),
|
34
|
+
tools: (@tools.map(&:serialize) if @tools&.any?),
|
35
|
+
}).compact
|
36
|
+
end
|
37
|
+
|
38
|
+
# @return [String]
|
39
|
+
def path
|
40
|
+
"/chat/completions"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OmniAI
|
4
|
+
module DeepSeek
|
5
|
+
# An DeepSeek client implementation. Usage:
|
6
|
+
#
|
7
|
+
# w/ `api_key``:
|
8
|
+
# client = OmniAI::DeepSeek::Client.new(api_key: '...')
|
9
|
+
#
|
10
|
+
# w/ ENV['DEEPSEEK_API_KEY']:
|
11
|
+
#
|
12
|
+
# ENV['DEEPSEEK_API_KEY'] = '...'
|
13
|
+
# client = OmniAI::DeepSeek::Client.new
|
14
|
+
#
|
15
|
+
# w/ config:
|
16
|
+
#
|
17
|
+
# OmniAI::DeepSeek.configure do |config|
|
18
|
+
# config.api_key = '...'
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# client = OmniAI::DeepSeek::Client.new
|
22
|
+
class Client < OmniAI::Client
|
23
|
+
# @param api_key [String, nil] optional - defaults to `OmniAI::DeepSeek.config.api_key`
|
24
|
+
# @param host [String] optional - defaults to `OmniAI::DeepSeek.config.host`
|
25
|
+
# @param logger [Logger, nil] optional - defaults to `OmniAI::DeepSeek.config.logger`
|
26
|
+
# @param timeout [Integer, nil] optional - defaults to `OmniAI::DeepSeek.config.timeout`
|
27
|
+
def initialize(
|
28
|
+
api_key: OmniAI::DeepSeek.config.api_key,
|
29
|
+
host: OmniAI::DeepSeek.config.host,
|
30
|
+
logger: OmniAI::DeepSeek.config.logger,
|
31
|
+
timeout: OmniAI::DeepSeek.config.timeout
|
32
|
+
)
|
33
|
+
super
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [HTTP::Client]
|
37
|
+
def connection
|
38
|
+
@connection ||= begin
|
39
|
+
http = super
|
40
|
+
http = http.auth("Bearer #{@api_key}") if @api_key
|
41
|
+
http
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# @raise [OmniAI::Error]
|
46
|
+
#
|
47
|
+
# @param messages [String] optional
|
48
|
+
# @param model [String] optional
|
49
|
+
# @param format [Symbol] optional :text or :json
|
50
|
+
# @param temperature [Float, nil] optional
|
51
|
+
# @param stream [Proc, nil] optional
|
52
|
+
# @param tools [Array<OmniAI::Tool>, nil] optional
|
53
|
+
#
|
54
|
+
# @yield [prompt]
|
55
|
+
# @yieldparam prompt [OmniAI::Chat::Prompt]
|
56
|
+
#
|
57
|
+
# @return [OmniAI::Chat::Completion]
|
58
|
+
def chat(messages = nil, model: Chat::DEFAULT_MODEL, temperature: nil, format: nil, stream: nil, tools: nil, &)
|
59
|
+
Chat.process!(messages, model:, temperature:, format:, stream:, tools:, client: self, &)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OmniAI
|
4
|
+
module DeepSeek
|
5
|
+
# Configuration for DeepSeek.
|
6
|
+
class Config < OmniAI::Config
|
7
|
+
DEFAULT_HOST = "https://api.deepseek.com"
|
8
|
+
|
9
|
+
# @param api_key [String, nil] optional - defaults to `ENV['DEEPSEEK_API_KEY']`
|
10
|
+
# @param host [String, nil] optional - defaults to ENV['DEEPSEEK_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("DEEPSEEK_API_KEY", nil),
|
15
|
+
host: ENV.fetch("DEEPSEEK_HOST", DEFAULT_HOST),
|
16
|
+
logger: nil,
|
17
|
+
timeout: nil
|
18
|
+
)
|
19
|
+
super
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,25 @@
|
|
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.inflector.inflect "deepseek" => "DeepSeek"
|
10
|
+
loader.setup
|
11
|
+
|
12
|
+
module OmniAI
|
13
|
+
# A namespace for everything DeepSeek.
|
14
|
+
module DeepSeek
|
15
|
+
# @return [OmniAI::DeepSeek::Config]
|
16
|
+
def self.config
|
17
|
+
@config ||= Config.new
|
18
|
+
end
|
19
|
+
|
20
|
+
# @yield [OmniAI::DeepSeek::Config]
|
21
|
+
def self.configure
|
22
|
+
yield config
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: omniai-deepseek
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kevin Sylvestre
|
8
|
+
bindir: exe
|
9
|
+
cert_chain: []
|
10
|
+
date: 2025-01-30 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: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
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 DeepSeek
|
55
|
+
email:
|
56
|
+
- kevin@ksylvest.com
|
57
|
+
executables: []
|
58
|
+
extensions: []
|
59
|
+
extra_rdoc_files: []
|
60
|
+
files:
|
61
|
+
- Gemfile
|
62
|
+
- README.md
|
63
|
+
- lib/omniai/deepseek.rb
|
64
|
+
- lib/omniai/deepseek/chat.rb
|
65
|
+
- lib/omniai/deepseek/client.rb
|
66
|
+
- lib/omniai/deepseek/config.rb
|
67
|
+
- lib/omniai/deepseek/version.rb
|
68
|
+
homepage: https://github.com/ksylvest/omniai-deepseek
|
69
|
+
licenses:
|
70
|
+
- MIT
|
71
|
+
metadata:
|
72
|
+
homepage_uri: https://github.com/ksylvest/omniai-deepseek
|
73
|
+
changelog_uri: https://github.com/ksylvest/omniai-deepseek/releases
|
74
|
+
rubygems_mfa_required: 'true'
|
75
|
+
rdoc_options: []
|
76
|
+
require_paths:
|
77
|
+
- lib
|
78
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 3.2.0
|
83
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
requirements: []
|
89
|
+
rubygems_version: 3.6.2
|
90
|
+
specification_version: 4
|
91
|
+
summary: A generalized framework for interacting with DeepSeek
|
92
|
+
test_files: []
|