mistral-ai 1.0.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.
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'event_stream_parser'
4
+ require 'faraday'
5
+ require 'json'
6
+
7
+ require_relative '../ports/dsl/mistral-ai/errors'
8
+
9
+ module Mistral
10
+ module Controllers
11
+ class Client
12
+ DEFAULT_ADDRESS = 'https://api.mistral.ai'
13
+
14
+ ALLOWED_REQUEST_OPTIONS = %i[timeout open_timeout read_timeout write_timeout].freeze
15
+
16
+ def initialize(config)
17
+ @api_key = config.dig(:credentials, :api_key)
18
+ @server_sent_events = config.dig(:options, :server_sent_events)
19
+
20
+ @address = if config[:credentials][:address].nil? || config[:credentials][:address].to_s.strip.empty?
21
+ "#{DEFAULT_ADDRESS}/"
22
+ else
23
+ "#{config[:credentials][:address].to_s.sub(%r{/$}, '')}/"
24
+ end
25
+
26
+ if @api_key.nil? && @address == "#{DEFAULT_ADDRESS}/"
27
+ raise MissingAPIKeyError, 'Missing API Key, which is required.'
28
+ end
29
+
30
+ @request_options = config.dig(:options, :connection, :request)
31
+
32
+ @request_options = if @request_options.is_a?(Hash)
33
+ @request_options.select do |key, _|
34
+ ALLOWED_REQUEST_OPTIONS.include?(key)
35
+ end
36
+ else
37
+ {}
38
+ end
39
+ end
40
+
41
+ def chat_completions(payload, server_sent_events: nil, &callback)
42
+ server_sent_events = false if payload[:stream] != true
43
+ request('v1/chat/completions', payload, server_sent_events:, &callback)
44
+ end
45
+
46
+ def embeddings(payload, server_sent_events: nil, &callback)
47
+ request('v1/embeddings', payload, server_sent_events: false, &callback)
48
+ end
49
+
50
+ def request(path, payload, server_sent_events: nil, &callback)
51
+ server_sent_events_enabled = server_sent_events.nil? ? @server_sent_events : server_sent_events
52
+ url = "#{@address}/#{path}"
53
+
54
+ if !callback.nil? && !server_sent_events_enabled
55
+ raise BlockWithoutServerSentEventsError,
56
+ 'You are trying to use a block without Server Sent Events (SSE) enabled.'
57
+ end
58
+
59
+ results = []
60
+
61
+ response = Faraday.new(request: @request_options) do |faraday|
62
+ faraday.response :raise_error
63
+ end.post do |request|
64
+ request.url url
65
+ request.headers['Content-Type'] = 'application/json'
66
+
67
+ request.headers['Authorization'] = "Bearer #{@api_key}" unless @api_key.nil?
68
+
69
+ request.body = payload.to_json
70
+
71
+ if server_sent_events_enabled
72
+ parser = EventStreamParser::Parser.new
73
+
74
+ request.options.on_data = proc do |chunk, bytes, env|
75
+ if env && env.status != 200
76
+ raise_error = Faraday::Response::RaiseError.new
77
+ raise_error.on_complete(env.merge(body: chunk))
78
+ end
79
+
80
+ parser.feed(chunk) do |type, data, id, reconnection_time|
81
+ parsed_data = safe_parse_json(data)
82
+
83
+ if parsed_data != '[DONE]'
84
+ result = {
85
+ event: safe_parse_json(data),
86
+ parsed: { type:, data:, id:, reconnection_time: },
87
+ raw: { chunk:, bytes:, env: }
88
+ }
89
+
90
+ callback.call(result[:event], result[:parsed], result[:raw]) unless callback.nil?
91
+
92
+ results << result
93
+
94
+ parsed_data['choices'].find do |candidate|
95
+ !candidate['finish_reason'].nil? && candidate['finish_reason'] != ''
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+
103
+ return safe_parse_json(response.body) unless server_sent_events_enabled
104
+
105
+ results.map { |result| result[:event] }
106
+ rescue Faraday::ServerError => e
107
+ raise RequestError.new(e.message, request: e, payload:)
108
+ end
109
+
110
+ def safe_parse_json(raw)
111
+ raw.start_with?('{', '[') ? JSON.parse(raw) : raw
112
+ rescue JSON::ParserError
113
+ raw
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'static/gem'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = Mistral::GEM[:name]
7
+ spec.version = Mistral::GEM[:version]
8
+ spec.authors = [Mistral::GEM[:author]]
9
+
10
+ spec.summary = Mistral::GEM[:summary]
11
+ spec.description = Mistral::GEM[:description]
12
+
13
+ spec.homepage = Mistral::GEM[:github]
14
+
15
+ spec.license = Mistral::GEM[:license]
16
+
17
+ spec.required_ruby_version = Gem::Requirement.new(">= #{Mistral::GEM[:ruby]}")
18
+
19
+ spec.metadata['allowed_push_host'] = Mistral::GEM[:gem_server]
20
+
21
+ spec.metadata['homepage_uri'] = spec.homepage
22
+ spec.metadata['source_code_uri'] = Mistral::GEM[:github]
23
+
24
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
25
+ `git ls-files -z`.split("\x0").reject do |f|
26
+ f.match(%r{\A(?:test|spec|features)/})
27
+ end
28
+ end
29
+
30
+ spec.require_paths = ['ports/dsl']
31
+
32
+ spec.add_dependency 'event_stream_parser', '~> 1.0'
33
+ spec.add_dependency 'faraday', '~> 2.8', '>= 2.8.1'
34
+
35
+ spec.metadata['rubygems_mfa_required'] = 'true'
36
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../components/errors'
4
+
5
+ include Mistral::Errors
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../static/gem'
4
+ require_relative '../../controllers/client'
5
+
6
+ module Mistral
7
+ def self.new(...)
8
+ Controllers::Client.new(...)
9
+ end
10
+
11
+ def self.version
12
+ Mistral::GEM[:version]
13
+ end
14
+ end
data/static/gem.rb ADDED
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mistral
4
+ GEM = {
5
+ name: 'mistral-ai',
6
+ version: '1.0.0',
7
+ author: 'gbaptista',
8
+ summary: 'Interact with Mistral AI.',
9
+ description: "A Ruby gem for interacting with Mistral AI's large language models.",
10
+ github: 'https://github.com/gbaptista/mistral-ai',
11
+ gem_server: 'https://rubygems.org',
12
+ license: 'MIT',
13
+ ruby: '3.1.0'
14
+ }.freeze
15
+ end
@@ -0,0 +1,39 @@
1
+ (require '[clojure.string :as str])
2
+
3
+ (defn slugify [text]
4
+ (-> text
5
+ (clojure.string/lower-case)
6
+ (clojure.string/replace " " "-")
7
+ (clojure.string/replace #"[^a-z0-9\-_]" "")))
8
+
9
+ (defn remove-code-blocks [content]
10
+ (let [code-block-regex #"(?s)```.*?```"]
11
+ (clojure.string/replace content code-block-regex "")))
12
+
13
+ (defn process-line [line]
14
+ (when-let [[_ hashes title] (re-find #"^(\#{2,}) (.+)" line)]
15
+ (let [link (slugify title)]
16
+ {:level (count hashes) :title title :link link})))
17
+
18
+ (defn create-index [content]
19
+ (let [processed-content (remove-code-blocks content)
20
+ processed-lines (->> processed-content
21
+ clojure.string/split-lines
22
+ (map process-line)
23
+ (remove nil?))]
24
+ (->> processed-lines
25
+ (map (fn [{:keys [level title link]}]
26
+ (str (apply str (repeat (* 4 (- level 2)) " "))
27
+ "- ["
28
+ title
29
+ "](#"
30
+ link
31
+ ")")))
32
+ (clojure.string/join "\n"))))
33
+
34
+
35
+ (let [content (slurp "template.md")
36
+ index (create-index content)
37
+ updated-content (clojure.string/replace content "{index}" index)]
38
+ (spit "README.md" updated-content)
39
+ (println "README.md successfully generated."))