• GPU 不是问题,Torch 和 Onnxruntime 都支持

    # For torch
    curl -L https://download.pytorch.org/libtorch/cpu/libtorch-macos-arm64-2.5.1.zip > libtorch.zip
    unzip -q libtorch.zip
    bundle config build.torch-rb --with-torch-dir=/path/to/libtorch
    gem install torch-rb
    Torch.tensor(1.0).to("cuda")
    
    # For onnxruntime
    # Download gpu version https://github.com/microsoft/onnxruntime/releases
    OnnxRuntime.ffi_lib = "path/to/lib/libonnxruntime.so"
    model = OnnxRuntime::Model.new("/path/to/model.onnx", providers: ["CUDAExecutionProvider"])
    

    然后代码再调试下即可,原代码设计成 DEVICE=cuda ruby demo.rb,不过没去调试。

    其它地方用过还是蛮丝滑的,只要显存够。

  • 对的,要根据场景自己确定怎么应用,比如 redis 的 redlock 选择不占着卡死进程而是超时释放,代码逻辑就要兜底处理这个 lock 被释放时的处理。

  • redis 作者对这篇文章有回复的,也写的有些道理,有兴趣可以自己找着看

  • 业务无关的纯技术内部实现的复用部分 -> concerns 业务有关的复用代码 -> services

  • 羡慕 UI 能做这么好看

  • 用的什么技术栈?ActionCable?IM 框架?

  • 分布式锁 at 2024年05月26日

    已经分布式锁了就不需要叠加 synchronized 了吧。 不同分布式锁实现的超时处理机制不一样。 gem 的话,redis 的 redlock,pg 的 with_advisory_lock,都可以拿来就用。

  • 之前用了 OpenResty,接收所有域名指向到默认 Rails 应用(没有做数据隔离),配置自动获取 Let's Encrypt 就可用。

    每一个客户有需要自己域名 cname 过来即可。

    问题是有大量莫名的乱七八糟的域名解析过来,导致大量无效的证书获取触发 Let's encrypt 的 limit 限制。

    还在找更好的方案

  • 再见啦,Ruby on Rails at 2023年09月10日

    提另外一个担心又欣慰的事,在大家喊引入静态类型的时候,matz 坚持不在 Ruby 中引入类型,坚持把 type 隔离在外部库来支持。

  • linux, vim, rvm, firefox, 就这么多,本机开发调试。

    pg/redis/elastic_search/qdrant/Rabbit MQ/Kafka等各种配套服务可以考虑docker直接起服务; 一般超稳定的软件比如 pg 我会本机直接安装,各种不确定的用 docker。

  • 都 NLUI 了,前后端分离是啥

  • 勿执勿念,放弃放下。

  • 我可以比年轻人拿得少一点(不止一点),

    摆正心态,打工挣钱,难受都是自己为难自己。

  • 国内 Ruby 圈子真的很小 at 2023年04月18日

    那就是我没用好(悲伤)

  • 一个奇葩的问题 at 2023年04月18日

    这是直接用 sidekiq(include Sidekiq::Job)会发生的问题,

    还是基于 ActiveJob 使用也会发生的问题?

  • 第一次看到这么惊喜的年龄要求

  • 把它设计成 resource,then restful

  • 管理层也只是一个工作,没什么不一样。

  • 遵守,挺好的。

  • ChatClient 就是一个 Faraday.new,太简单了所以没贴。细节在这里: https://github.com/as181920/openai_api_proxy/blob/main/lib/openai_api_proxy/chat_client.rb

  • 前些天在老产品上面也添加了一个给老用户体验的, https://www.vcooline.com

    主要加了返回 stream 的处理(一个字一个字蹦出来)

    def perform_openai_request(prompt:)
      completion_content = ""
      start_chunk_message
      OpenaiApiProxy::ChatClient.new(api_key: ‘’, organization_id: ‘’).create(
        model: "gpt-3.5-turbo",
        messages: generate_chat_messages(prompt),
        max_tokens:1234
        user: user.to_gid.to_s,
        stream: true
      ) do |reqt|
        reqt.options.on_data = Proc.new do |chunk, overall_received_bytes, env|
          chunk_contents = handle_openai_chunk_data(chunk, overall_received_bytes, env)
          chunk_contents.compact_blank.map { |chunk_content| completion_content.concat(chunk_content) }
        end 
      end 
    
      completion_content.presence
        &.tap { |content| decrease_usage_quota(content:) }
        &.tap { |content| save_bot_message(content:) }
    rescue OpenaiApiProxy::Client::ServerError, OpenaiApiProxy::Client::InternalError => e
      Rails.logger.error "#{self.class.name} perform #{e.class.name}: #{e.message}"
      send_chunk_message(content: "系统繁忙,请稍候再试。")
    end 
    
    def handle_openai_chunk_data(chunk, _overall_received_bytes, _env)
      chunk.force_encoding("UTF-8").encode("UTF-8", invalid: :replace, undef: :replace, replace: "") 
        .split("\n\n")
        .map { |line| line.sub(/^data:\ */, "") }
        .compact_blank
        .map.each_with_object([]) do |chunk_line, chunk_contents|
          break chunk_contents if chunk_line == "[DONE]"
    
          chunk_info = JSON.parse chunk_line
          # start_chunk_message if chunk_info.dig("choices", 0, "delta", "role").eql?("assistant")
          chunk_info.dig("choices", 0, "delta", "content").presence
            &.tap { |chunk_content| chunk_contents.push(chunk_content) }
            &.tap { |chunk_content| send_chunk_message(content: chunk_content) }
        end 
    end 
    
    def send_chunk_message(content:)
      start_chunk_input
      message.broadcast_append_to \
        ActionView::RecordIdentifier.dom_id(message.conversation, :namespace_example),
        target: reply_message_dom_id,
        html: content
    end 
    
    
  • 效率真厉害👍

  • http.get(api).body.dig "choices", 0, "text" 是不是大概就这么一行的事情

  • 也不是一定要购买云服务,一些基础的软件 redis/pg/mq 等自建还是蛮稳定的,看业务要求到什么程度。

  • Ruby 工具书

    Rails Guide(类似于本科学经济学原理,研究生学经济学原理,博士还是学经济学原理)

    源码不着急

  • 如果有 turbo 但没有 stimulus 的情况,可以考虑

    document.addEventListener("turbo:load", function() 
    document.addEventListener("turbo:frame-load", function(ev)
    

    来做初始化

  • Rails 应用的部署管理 at 2022年08月13日

    这边一直是这么干的,所有部署全部是 production env。走 cap 的 linked_file 文件(并转化成 ENV 参数),来区分不同环境需要的不同配置。

     class Application < Rails::Application
        config.before_configuration do
            env_file = Rails.root.join("config/local_env.yml")
            YAML.load_file(env_file, aliases: true)[Rails.env].to_h.each do |key, value|
              ENV[key.to_s] = value.to_s
            end if File.file?(env_file)
          end 
    end
    
  • 关于聚合支付 at 2022年03月23日

    这是汇付天下的产品吧,我去了解了解

  • 关于聚合支付 at 2022年03月22日

    最近遇到分账需求,调研了一下费用普遍过高,不适合中小商家使用,目前考虑单独对接微信&支付宝。

    在这两大平台提供原生分账能力的情况下,不清楚现在聚合支付机构收取那么高的年费是基于什么缘故(涉及分账就贵,牌照虽然贵,另外没牌照走银行托管账户方案的也贵)。

  • / fix disable_with on link and submit inside turbo-frame
    Rails.delegate(document, Rails.linkDisableSelector,   "turbo:before-cache", Rails.enableElement)
    Rails.delegate(document, Rails.buttonDisableSelector, "turbo:before-cache", Rails.enableElement)
    Rails.delegate(document, Rails.buttonDisableSelector, "turbo:submit-end", Rails.enableElement)
    Rails.delegate(document, Rails.formSubmitSelector, "turbo:submit-start", Rails.disableElement)
    Rails.delegate(document, Rails.formSubmitSelector, "turbo:submit-end", Rails.enableElement)
    Rails.delegate(document, Rails.formSubmitSelector, "turbo:before-cache", Rails.enableElement)