mistral-ai 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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."))