Files

1 line
24 KiB
JSON
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"repo": "ahoward/drawohara.io", "files": {"lib/ai.rb": {"9730e5788cb5989391c6bd89a408e718f0d1ca42": {"t": 1785745744.606673, "n": 11335, "h": "630529e79df2f7bf", "c": "require 'bundler/setup'\n\nrequire 'thread'\nrequire 'json'\nrequire 'time'\nrequire 'securerandom'\nrequire 'fileutils'\n\nrequire 'map'\nrequire 'anthropic'\nrequire 'groq'\nrequire 'mistral-ai'\nrequire 'mistral-ai/errors'\n\n\nrequire_relative 'rate_limiter.rb'\nrequire_relative 'env.rb'\n\nmodule AI\n def AI.provider\n AI::Groq\n #AI::Mistral\n #AI::Anthropic\n #AI::DeepSeek\n end\n\n def AI.completion_for(prompt, temperature:0.7, format:'txt')\n provider.completion_for(prompt, temperature:, format:)\n end\n\n def AI.json_parse_liberally(json)\n begin\n JSON.parse(json)\n rescue => error\n begin\n json.gsub!('```json', '')\n json.gsub!('```', '')\n JSON.parse(json)\n rescue\n raise error\n end\n end\n end\n\n def AI.count_tokens(*args)\n string = args.join(\"\\n\")\n words = string.scan(/\\w+/)\n words_per_token = 3.0/4.0\n (words.size * 1/words_per_token).to_i\n end\n\n class Groq\n @@API_KEY = ENV.fetch('GROQ_API_KEY')\n @@MODEL = 'llama-3.3-70b-versatile'\n #@@MODEL = 'compound-beta'\n @@MAX_TOKENS = 128_000\n @@TIMEOUT = 420\n\n @@RPM = 30 #60\n @@RATE_LIMTER = RateLimiter.new(name: 'groq', rpm: @@RPM - 1)\n\n attr_reader :client\n\n def initialize(api_key: @@API_KEY, timeout: @@TIMEOUT)\n @api_key = api_key\n @timeout = timeout\n @client = ::Groq::Client.new(api_key: @api_key, model_id: @@MODEL, timeout: @timeout)\n end\n\n def completion_for(*args, role:'user', system:nil, prompt:nil, temperature:nil, format:nil)\n content = [prompt || args].join(\"\\n\")\n Groq.try_hard do\n Groq.rate_limit do\n client.chat(content).fetch('content')\n end\n end\n end\n\n def Groq.completion_for(...)\n new.completion_for(...)\n end\n\n def Groq.rate_limit(&block)\n @@RATE_LIMTER.limit(&block)\n end\n\n def Groq.try_hard(*args, &block)\n if @try_hard == false\n return block.call\n end\n\n n = 6\n errors = []\n fatal = [\n RangeError,\n NameError,\n ArgumentError,\n Faraday::BadRequestError,\n Faraday::ClientError\n ]\n\n n.times do |i|\n begin\n return block.call\n rescue => error\n raise error if fatal.include?(error.class)\n errors.push(error)\n s = (2 ** (i + 2))\n warn \"Groq.try_hard: sleep(#{ s }), #{ error.class }[#{ error.message }]\"\n sleep(s)\n end\n end\n\n raise errors.last\n end\n\n def Groq.try_hard=(try_hard)\n @try_hard = !!try_hard\n end\n end\n\n class Anthropic\n @@API_KEY = ENV.fetch('ANTHROPIC_API_KEY')\n @@MODEL = 'claude-3-5-sonnet-latest'\n @@MAX_TOKENS = 42420\n @@TIMEOUT = 420\n\n @@RPM = 50\n @@RATE_LIMTER = RateLimiter.new(name: 'anthropic', rpm: @@RPM - 1)\n\n attr_reader :client\n\n def initialize(api_key: @@API_KEY, timeout: @@TIMEOUT)\n @api_key = api_key\n @timeout = timeout\n @client = ::Anthropic::Client.new(access_token: @api_key, request_timeout: @timeout)\n end\n\n def chat_completions(parameters = {}, **kws)\n parameters = Map.for(parameters).update(kws)\n\n parameters.keys.each do |key|\n parameters.delete(key) if parameters[key].nil?\n end\n\n parameters[:model] = @@MODEL unless parameters.has_key?(:model)\n\n parameters[:max_tokens] = @@MAX_TOKENS unless parameters.has_key?(:max_tokens)\n\n Anthropic.try_hard do\n Anthropic.rate_limit do\n Map.for(client.messages(parameters:))\n end\n end\n end\n\n def Anthropic.completion_for(...)\n new.completion_for(...)\n end\n\n def completion_for(*args, role:'user', system:nil, prompt:nil, temperature:nil, format:nil)\n content = [prompt || args].join(\"\\n\")\n messages = []\n messages << {role:, content:}\n result = chat_completions(system:, messages:)\n result.get(:content, 0, :text)\n end\n\n def Anthropic.rate_limit(&block)\n @@RATE_LIMTER.limit(&block)\n end\n\n def Anthropic.try_hard(*args, &block)\n if @try_hard == false\n return block.call\n end\n\n n = 7\n errors = []\n fatal = [\n Faraday::BadRequestError,\n Faraday::ClientError\n ]\n\n n.times do |i|\n begin\n return block.call\n rescue => error\n raise error if fatal.include?(error.class)\n errors.push(error)\n s = (2 ** i)\n warn \"Anthropic.try_hard: sleep(#{ s }), #{ error.class }[#{ error.message }]\"\n sleep(s)\n end\n end\n\n raise errors.last\n end\n\n def Anthropic.try_hard=(try_hard)\n @try_hard = !!try_hard\n end\n end\n\n class DeepSeek\n @@BASE_URL = 'https://api.deepseek.com'\n @@API_KEY = ENV.fetch('DEEPSEEK_API_KEY')\n @@MODEL = 'deepseek-reasoner'\n @@TIMEOUT = 420\n\n def initialize(api_key: @@API_KEY, timeout: @@TIMEOUT)\n @api_key = api_key\n @timeout = timeout\n end\n\n def chat_completions(params = {}, **kws)\n params = Map.for(params).update(kws)\n params[:model] = @@MODEL unless params.has_key?(:model)\n params[:stream] = false unless params.has_key?(:stream)\n\n=begin\n data = params.to_hash.to_json\n cmd = \"curl https://api.deepseek.com/chat/completions -s -H 'Content-Type: application/json' -H 'Authorization: Bearer #{ @api_key }' -d '#{ data }'\"\n puts cmd\n oe = `#{ cmd }`\n return Map.for(JSON.parse(oe))\n=end\n\n uri = URI.parse('https://api.deepseek.com/chat/completions')\n http = Net::HTTP.new(uri.host, uri.port)\n http.use_ssl = true\n\n request = Net::HTTP::Post.new(uri.path)\n request['Content-Type'] = 'application/json'\n request['Authorization'] = 'Bearer %s' % @api_key\n\n request.body = params.to_json\n\n response = http.request(request)\n\n if response.is_a?(Net::HTTPSuccess)\n Map.for(JSON.parse(response.body))\n else\n raise \"Error: #{ response.code } - #{ response.message }\\n\\n#{ response.body }\"\n end\n end\n\n def DeepSeek.completion_for(...)\n result = new.completion_for(...)\n end\n\n def completion_for(*args, system:nil, prompt:nil, temperature:nil, format:nil)\n user = [prompt || args].join(\"\\n\")\n\n messages = []\n\n if system\n messages << {role: 'system', content: [system].join(\"\\n\")}\n end\n\n messages << {role: 'user', content: user}\n\n result = chat_completions(messages:)\n\n result.get(:choices, 0, :message, :content)\n end\n\n=begin\n{\"id\"=>\"6a619c81-0ea7-4b98-8a82-2fdd886e601a\",\n \"object\"=>\"chat.completion\",\n \"created\"=>1738471159,\n \"model\"=>\"deepseek-reasoner\",\n \"choices\"=>\n [{\"index\"=>0,\n \"message\"=>\n {\"role\"=>\"assistant\",\n \"content\"=>\"Sure! Here's a light-hearted one: \\n\\nWhy don’t scientists trust atoms? \\n…Because they *make up everything*! \\n\\n(Atomic puns are the *element* of a good joke, right?) 😄\",\n \"reasoning_content\"=>\n \"Okay, the user asked for a joke. I need to come up with something light-hearted and appropriate. Let's see, maybe something tech-related since I'm an AI. How about a classic setup with a pun.\\n\\nWhy don't scientists trust atoms? Because they make up everything! That's a good one. It's simple, has a pun, and relates to science which ties into the AI theme. Let me check if there's any chance it might be offensive or not. No, atoms are a safe topic. Alright, that should work.\"},\n \"logprobs\"=>nil,\n \"finish_reason\"=>\"stop\"}],\n \"usage\"=>\n {\"prompt_tokens\"=>9,\n \"completion_tokens\"=>161,\n \"total_tokens\"=>170,\n \"prompt_tokens_details\"=>{\"cached_tokens\"=>0},\n \"completion_tokens_details\"=>{\"reasoning_tokens\"=>112},\n \"prompt_cache_hit_tokens\"=>0,\n \"prompt_cache_miss_tokens\"=>9},\n \"system_fingerprint\"=>\"fp_7e73fd9a08\"}\n=end\n end\n\n class Mistral\n @@API_KEY = ENV.fetch('MISTRAL_API_KEY')\n @@MODEL = 'pixtral-large-latest'\n @@TIMEOUT = 420\n\n @@RPS = 20\n @@RATE_LIMTER = RateLimiter.new(name: 'mistral', rps: @@RPS - 1)\n\n attr_reader :client\n\n def initialize(api_key: @@API_KEY, timeout: @@TIMEOUT)\n @api_key = api_key\n @timeout = timeout\n @client = ::Mistral.new(\n credentials: { api_key: @api_key },\n options: {\n connection: { request: { timeout: @timeout } },\n }\n )\n end\n\n %w[\n embeddings\n chat_completions\n agent_chat_completions\n ].each do |method|\n class_eval <<-____, __FILE__, __LINE__ + 1\n def #{ method }(*args, **kws, &block)\n Mistral.try_hard do\n result =\n Mistral.rate_limit do\n client.#{ method }(*args, **kws, &block)\n end\n\n return Map.for(result)\n end\n end\n ____\n end\n\n def Mistral.completion_for(prompt, temperature:0.7, model:@@MODEL, format: 'txt')\n mistral = new\n\n messages =\n [\n {\n \"role\": \"user\",\n \"content\": [\n {\n \"type\": \"text\",\n \"text\": prompt.to_s,\n },\n ]\n },\n ]\n\n params = {\n temperature: temperature,\n model: model,\n messages: messages,\n }\n\n if format.to_s == 'json'\n params[:response_format] = {type: \"json_object\"}\n end\n\n result =\n case\n when model =~ /^ag:/\n mistral.agent_completions(params)\n else\n mistral.chat_completions(params)\n end\n\n return result.get(:choices, 0, :message, :content)\n end\n\n def embeddings_for(input, *inputs, model: 'mistral-embed')\n inputs =\n [input.to_s, *inputs.map(&:to_s)]\n\n result =\n embeddings(\n { model: model,\n input: inputs,\n }\n )\n\n inputs.size.times.map do |i|\n result.fetch(:data).fetch(i).fetch(:embedding)\n end\n end\n\n def embedding_for(input, model: 'mistral-embed')\n embeddings_for(input, model:).first\n end\n\n def Mistral.embeddings_for(...)\n new.embeddings_for(...)\n end\n\n def Mistral.embedding_for(...)\n new.embedding_for(...)\n end\n\n def Mistral.rate_limit(&block)\n @@RATE_LIMTER.limit(&block)\n end\n\n def Mistral.try_hard(*args, &block)\n if @try_hard == false\n return block.call\n end\n\n n = 7\n errors = []\n\n n.times do |i|\n begin\n return block.call\n rescue ::Mistral::Errors::RequestError, Faraday::TooManyRequestsError => error\n errors.push(error)\n s = (2 ** i)\n warn \"Mistral.try_hard: sleep(#{ s }), #{ error.class }[#{ error.message }]\"\n sleep(s)\n end\n end\n\n raise errors.last\n end\n\n def Mistral.try_hard=(try_hard)\n @try_hard = !!try_hard\n end\n end\nend\n\n# monkey-patch\nmodule Mistral\n module Controllers\n class Client\n def agent_chat_completions(payload, server_sent_events: nil, &callback)\n server_sent_events = false if payload[:stream] != true\n request('v1/agents/completions', payload, server_sent_events:, &callback)\n end\n end\n end\nend\n\n class LLama\n #completion = AI::Mistral.try_hard{ AI::Mistral.completion_for(prompt) }\n #cmd = \"ollama run llama3.2 /dev/stdin\"\n #stdin = prompt\n #stdout = IO.popen(cmd, 'w+'){|io| io.write(stdin); io.close_write; io.read}\n #stdout.strip\n end\n"}, "bfd44c562165a80cd2197d4cd62ef88e47ffefd8": {"t": 1785756104.1107695, "n": 11335, "h": "630529e79df2f7bf", "c": "require 'bundler/setup'\n\nrequire 'thread'\nrequire 'json'\nrequire 'time'\nrequire 'securerandom'\nrequire 'fileutils'\n\nrequire 'map'\nrequire 'anthropic'\nrequire 'groq'\nrequire 'mistral-ai'\nrequire 'mistral-ai/errors'\n\n\nrequire_relative 'rate_limiter.rb'\nrequire_relative 'env.rb'\n\nmodule AI\n def AI.provider\n AI::Groq\n #AI::Mistral\n #AI::Anthropic\n #AI::DeepSeek\n end\n\n def AI.completion_for(prompt, temperature:0.7, format:'txt')\n provider.completion_for(prompt, temperature:, format:)\n end\n\n def AI.json_parse_liberally(json)\n begin\n JSON.parse(json)\n rescue => error\n begin\n json.gsub!('```json', '')\n json.gsub!('```', '')\n JSON.parse(json)\n rescue\n raise error\n end\n end\n end\n\n def AI.count_tokens(*args)\n string = args.join(\"\\n\")\n words = string.scan(/\\w+/)\n words_per_token = 3.0/4.0\n (words.size * 1/words_per_token).to_i\n end\n\n class Groq\n @@API_KEY = ENV.fetch('GROQ_API_KEY')\n @@MODEL = 'llama-3.3-70b-versatile'\n #@@MODEL = 'compound-beta'\n @@MAX_TOKENS = 128_000\n @@TIMEOUT = 420\n\n @@RPM = 30 #60\n @@RATE_LIMTER = RateLimiter.new(name: 'groq', rpm: @@RPM - 1)\n\n attr_reader :client\n\n def initialize(api_key: @@API_KEY, timeout: @@TIMEOUT)\n @api_key = api_key\n @timeout = timeout\n @client = ::Groq::Client.new(api_key: @api_key, model_id: @@MODEL, timeout: @timeout)\n end\n\n def completion_for(*args, role:'user', system:nil, prompt:nil, temperature:nil, format:nil)\n content = [prompt || args].join(\"\\n\")\n Groq.try_hard do\n Groq.rate_limit do\n client.chat(content).fetch('content')\n end\n end\n end\n\n def Groq.completion_for(...)\n new.completion_for(...)\n end\n\n def Groq.rate_limit(&block)\n @@RATE_LIMTER.limit(&block)\n end\n\n def Groq.try_hard(*args, &block)\n if @try_hard == false\n return block.call\n end\n\n n = 6\n errors = []\n fatal = [\n RangeError,\n NameError,\n ArgumentError,\n Faraday::BadRequestError,\n Faraday::ClientError\n ]\n\n n.times do |i|\n begin\n return block.call\n rescue => error\n raise error if fatal.include?(error.class)\n errors.push(error)\n s = (2 ** (i + 2))\n warn \"Groq.try_hard: sleep(#{ s }), #{ error.class }[#{ error.message }]\"\n sleep(s)\n end\n end\n\n raise errors.last\n end\n\n def Groq.try_hard=(try_hard)\n @try_hard = !!try_hard\n end\n end\n\n class Anthropic\n @@API_KEY = ENV.fetch('ANTHROPIC_API_KEY')\n @@MODEL = 'claude-3-5-sonnet-latest'\n @@MAX_TOKENS = 42420\n @@TIMEOUT = 420\n\n @@RPM = 50\n @@RATE_LIMTER = RateLimiter.new(name: 'anthropic', rpm: @@RPM - 1)\n\n attr_reader :client\n\n def initialize(api_key: @@API_KEY, timeout: @@TIMEOUT)\n @api_key = api_key\n @timeout = timeout\n @client = ::Anthropic::Client.new(access_token: @api_key, request_timeout: @timeout)\n end\n\n def chat_completions(parameters = {}, **kws)\n parameters = Map.for(parameters).update(kws)\n\n parameters.keys.each do |key|\n parameters.delete(key) if parameters[key].nil?\n end\n\n parameters[:model] = @@MODEL unless parameters.has_key?(:model)\n\n parameters[:max_tokens] = @@MAX_TOKENS unless parameters.has_key?(:max_tokens)\n\n Anthropic.try_hard do\n Anthropic.rate_limit do\n Map.for(client.messages(parameters:))\n end\n end\n end\n\n def Anthropic.completion_for(...)\n new.completion_for(...)\n end\n\n def completion_for(*args, role:'user', system:nil, prompt:nil, temperature:nil, format:nil)\n content = [prompt || args].join(\"\\n\")\n messages = []\n messages << {role:, content:}\n result = chat_completions(system:, messages:)\n result.get(:content, 0, :text)\n end\n\n def Anthropic.rate_limit(&block)\n @@RATE_LIMTER.limit(&block)\n end\n\n def Anthropic.try_hard(*args, &block)\n if @try_hard == false\n return block.call\n end\n\n n = 7\n errors = []\n fatal = [\n Faraday::BadRequestError,\n Faraday::ClientError\n ]\n\n n.times do |i|\n begin\n return block.call\n rescue => error\n raise error if fatal.include?(error.class)\n errors.push(error)\n s = (2 ** i)\n warn \"Anthropic.try_hard: sleep(#{ s }), #{ error.class }[#{ error.message }]\"\n sleep(s)\n end\n end\n\n raise errors.last\n end\n\n def Anthropic.try_hard=(try_hard)\n @try_hard = !!try_hard\n end\n end\n\n class DeepSeek\n @@BASE_URL = 'https://api.deepseek.com'\n @@API_KEY = ENV.fetch('DEEPSEEK_API_KEY')\n @@MODEL = 'deepseek-reasoner'\n @@TIMEOUT = 420\n\n def initialize(api_key: @@API_KEY, timeout: @@TIMEOUT)\n @api_key = api_key\n @timeout = timeout\n end\n\n def chat_completions(params = {}, **kws)\n params = Map.for(params).update(kws)\n params[:model] = @@MODEL unless params.has_key?(:model)\n params[:stream] = false unless params.has_key?(:stream)\n\n=begin\n data = params.to_hash.to_json\n cmd = \"curl https://api.deepseek.com/chat/completions -s -H 'Content-Type: application/json' -H 'Authorization: Bearer #{ @api_key }' -d '#{ data }'\"\n puts cmd\n oe = `#{ cmd }`\n return Map.for(JSON.parse(oe))\n=end\n\n uri = URI.parse('https://api.deepseek.com/chat/completions')\n http = Net::HTTP.new(uri.host, uri.port)\n http.use_ssl = true\n\n request = Net::HTTP::Post.new(uri.path)\n request['Content-Type'] = 'application/json'\n request['Authorization'] = 'Bearer %s' % @api_key\n\n request.body = params.to_json\n\n response = http.request(request)\n\n if response.is_a?(Net::HTTPSuccess)\n Map.for(JSON.parse(response.body))\n else\n raise \"Error: #{ response.code } - #{ response.message }\\n\\n#{ response.body }\"\n end\n end\n\n def DeepSeek.completion_for(...)\n result = new.completion_for(...)\n end\n\n def completion_for(*args, system:nil, prompt:nil, temperature:nil, format:nil)\n user = [prompt || args].join(\"\\n\")\n\n messages = []\n\n if system\n messages << {role: 'system', content: [system].join(\"\\n\")}\n end\n\n messages << {role: 'user', content: user}\n\n result = chat_completions(messages:)\n\n result.get(:choices, 0, :message, :content)\n end\n\n=begin\n{\"id\"=>\"6a619c81-0ea7-4b98-8a82-2fdd886e601a\",\n \"object\"=>\"chat.completion\",\n \"created\"=>1738471159,\n \"model\"=>\"deepseek-reasoner\",\n \"choices\"=>\n [{\"index\"=>0,\n \"message\"=>\n {\"role\"=>\"assistant\",\n \"content\"=>\"Sure! Here's a light-hearted one: \\n\\nWhy don’t scientists trust atoms? \\n…Because they *make up everything*! \\n\\n(Atomic puns are the *element* of a good joke, right?) 😄\",\n \"reasoning_content\"=>\n \"Okay, the user asked for a joke. I need to come up with something light-hearted and appropriate. Let's see, maybe something tech-related since I'm an AI. How about a classic setup with a pun.\\n\\nWhy don't scientists trust atoms? Because they make up everything! That's a good one. It's simple, has a pun, and relates to science which ties into the AI theme. Let me check if there's any chance it might be offensive or not. No, atoms are a safe topic. Alright, that should work.\"},\n \"logprobs\"=>nil,\n \"finish_reason\"=>\"stop\"}],\n \"usage\"=>\n {\"prompt_tokens\"=>9,\n \"completion_tokens\"=>161,\n \"total_tokens\"=>170,\n \"prompt_tokens_details\"=>{\"cached_tokens\"=>0},\n \"completion_tokens_details\"=>{\"reasoning_tokens\"=>112},\n \"prompt_cache_hit_tokens\"=>0,\n \"prompt_cache_miss_tokens\"=>9},\n \"system_fingerprint\"=>\"fp_7e73fd9a08\"}\n=end\n end\n\n class Mistral\n @@API_KEY = ENV.fetch('MISTRAL_API_KEY')\n @@MODEL = 'pixtral-large-latest'\n @@TIMEOUT = 420\n\n @@RPS = 20\n @@RATE_LIMTER = RateLimiter.new(name: 'mistral', rps: @@RPS - 1)\n\n attr_reader :client\n\n def initialize(api_key: @@API_KEY, timeout: @@TIMEOUT)\n @api_key = api_key\n @timeout = timeout\n @client = ::Mistral.new(\n credentials: { api_key: @api_key },\n options: {\n connection: { request: { timeout: @timeout } },\n }\n )\n end\n\n %w[\n embeddings\n chat_completions\n agent_chat_completions\n ].each do |method|\n class_eval <<-____, __FILE__, __LINE__ + 1\n def #{ method }(*args, **kws, &block)\n Mistral.try_hard do\n result =\n Mistral.rate_limit do\n client.#{ method }(*args, **kws, &block)\n end\n\n return Map.for(result)\n end\n end\n ____\n end\n\n def Mistral.completion_for(prompt, temperature:0.7, model:@@MODEL, format: 'txt')\n mistral = new\n\n messages =\n [\n {\n \"role\": \"user\",\n \"content\": [\n {\n \"type\": \"text\",\n \"text\": prompt.to_s,\n },\n ]\n },\n ]\n\n params = {\n temperature: temperature,\n model: model,\n messages: messages,\n }\n\n if format.to_s == 'json'\n params[:response_format] = {type: \"json_object\"}\n end\n\n result =\n case\n when model =~ /^ag:/\n mistral.agent_completions(params)\n else\n mistral.chat_completions(params)\n end\n\n return result.get(:choices, 0, :message, :content)\n end\n\n def embeddings_for(input, *inputs, model: 'mistral-embed')\n inputs =\n [input.to_s, *inputs.map(&:to_s)]\n\n result =\n embeddings(\n { model: model,\n input: inputs,\n }\n )\n\n inputs.size.times.map do |i|\n result.fetch(:data).fetch(i).fetch(:embedding)\n end\n end\n\n def embedding_for(input, model: 'mistral-embed')\n embeddings_for(input, model:).first\n end\n\n def Mistral.embeddings_for(...)\n new.embeddings_for(...)\n end\n\n def Mistral.embedding_for(...)\n new.embedding_for(...)\n end\n\n def Mistral.rate_limit(&block)\n @@RATE_LIMTER.limit(&block)\n end\n\n def Mistral.try_hard(*args, &block)\n if @try_hard == false\n return block.call\n end\n\n n = 7\n errors = []\n\n n.times do |i|\n begin\n return block.call\n rescue ::Mistral::Errors::RequestError, Faraday::TooManyRequestsError => error\n errors.push(error)\n s = (2 ** i)\n warn \"Mistral.try_hard: sleep(#{ s }), #{ error.class }[#{ error.message }]\"\n sleep(s)\n end\n end\n\n raise errors.last\n end\n\n def Mistral.try_hard=(try_hard)\n @try_hard = !!try_hard\n end\n end\nend\n\n# monkey-patch\nmodule Mistral\n module Controllers\n class Client\n def agent_chat_completions(payload, server_sent_events: nil, &callback)\n server_sent_events = false if payload[:stream] != true\n request('v1/agents/completions', payload, server_sent_events:, &callback)\n end\n end\n end\nend\n\n class LLama\n #completion = AI::Mistral.try_hard{ AI::Mistral.completion_for(prompt) }\n #cmd = \"ollama run llama3.2 /dev/stdin\"\n #stdin = prompt\n #stdout = IO.popen(cmd, 'w+'){|io| io.write(stdin); io.close_write; io.read}\n #stdout.strip\n end\n"}}}}