Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rename base executor to llama.cpp and added default config #51

Merged
merged 3 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions app/helpers/model_versions_helper.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
# frozen_string_literal: true

module ModelVersionsHelper
DEFAULT_CONFIGURATION = {
'openai' => '{"model":"gpt-3.5-turbo","temperature":0.5}',
'ollama' => '{"model":"llama3.1", "stream": false}',
'llama_cpp' => '{"n_predict":500,"temperature":0.5,"stop":["<|end|>","<|user|>","<|assistant|>","<|endoftext|>","<|system|>"]}'
}.freeze

def default_configuration(executor_type)
DEFAULT_CONFIGURATION[executor_type] || {}.to_json
end
end
2 changes: 2 additions & 0 deletions app/javascript/controllers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
import { application } from "controllers/application"
import AssertionController from "./assertion_controller"
import AssertionResultModalController from "./assertion_result_modal_controller"
import ModelController from "./model_controller"

// Eager load all controllers defined in the import map under controllers/**/*_controller
import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading"
eagerLoadControllersFrom("controllers", application)
application.register("assertion", AssertionController)
application.register("assertionResultModal", AssertionResultModalController)
application.register("model", ModelController)

// Lazy load controllers as they appear in the DOM (remember not to preload controllers in import map!)
// import { lazyLoadControllersFrom } from "@hotwired/stimulus-loading"
Expand Down
25 changes: 25 additions & 0 deletions app/javascript/controllers/model_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Controller } from "@hotwired/stimulus"
const DEFAULT_CONFIGURATION = {
openai: '{"model":"gpt-3.5-turbo","temperature":0.5}',
ollama: '{"model":"llama3.1", "stream": false}',
llama_cpp: '{"n_predict":500,"temperature":0.5,"stop":["<|end|>","<|user|>","<|assistant|>","<|endoftext|>","<|system|>"]}'
}

export default class extends Controller {
static targets = ["configurationField", "apiKeyField"]

connect() {
this.toggleConfiguration()
}

toggleConfiguration() {
const executorType = this.element.querySelector('#executor_type_selector').value
if (this.hasConfigurationFieldTarget)
this.configurationFieldTarget.value = DEFAULT_CONFIGURATION[executorType]

if (executorType == 'openai')
this.apiKeyFieldTarget.style.display = "block"
else
this.apiKeyFieldTarget.style.display = "none"
}
}
2 changes: 1 addition & 1 deletion app/models/model.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class Model < ApplicationRecord
has_many :model_versions, dependent: :destroy
accepts_nested_attributes_for :model_versions, allow_destroy: true, reject_if: :all_blank

enum :executor_type, { base: 0, openai: 1, ollama: 2 }, scopes: false, default: :base
enum :executor_type, { llama_cpp: 0, openai: 1, ollama: 2 }, scopes: false, default: :llama_cpp

default_scope { order(id: :desc) }
end
2 changes: 1 addition & 1 deletion app/models/model_version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def content_path
CONTENT_PATHS = {
'openai' => ['choices', 0, 'message', 'content'],
'ollama' => [0, 'response'],
'base' => ['content']
'llama_cpp' => ['content']
}.freeze

def configuration_is_json
Expand Down
6 changes: 6 additions & 0 deletions app/services/executors/llama_cpp.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# frozen_string_literal: true

module Executors
class LlamaCpp < Base
end
end
2 changes: 1 addition & 1 deletion app/views/model_versions/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

<div class="my-5">
<%= form.label :configuration %>
<%= form.text_field :configuration, value: (model_version&.configuration || {})&.to_json, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
<%= form.text_field :configuration, value: model_version&.configuration ? JSON::dump(model_version&.configuration) : default_configuration(@model.executor_type), class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
</div>

<div class="my-5">
Expand Down
8 changes: 4 additions & 4 deletions app/views/models/_form.html.erb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<%= form_with(model: model, class: "contents") do |form| %>
<%= form_with(model: model, class: "contents", data: { controller: "model" }) do |form| %>
<% if model.errors.any? %>
<div id="error_explanation" class="bg-red-50 text-red-500 px-3 py-2 font-medium rounded-lg mt-3">
<h2><%= pluralize(model.errors.count, "error") %> prohibited this model from being saved:</h2>
Expand All @@ -23,10 +23,10 @@

<div class="mb-4">
<%= form.label :executor_type, class: "block text-gray-700 text-sm font-bold mb-2" %>
<%= form.select :executor_type, Model.executor_types.keys.map { |w| [w.humanize, w] }, {}, class: "block appearance-none w-full bg-gray-200 border border-gray-200 text-gray-700 py-2 px-4 pr-8 rounded leading-tight focus:outline-none focus:bg-white focus:border-gray-500" %>
<%= form.select :executor_type, Model.executor_types.keys.map { |w| [I18n.t("activerecord.attributes.model.executor_types.#{w}"), w] }, {}, class: "block appearance-none w-full bg-gray-200 border border-gray-200 text-gray-700 py-2 px-4 pr-8 rounded leading-tight focus:outline-none focus:bg-white focus:border-gray-500", id: "executor_type_selector", data: { action: "change->model#toggleConfiguration" } %>
</div>

<div class="my-5">
<div id="api_key_field" data-model-target="apiKeyField" class="my-5" style="<%= model.executor_type == 'openai' ? '' : 'display:none;' %>">
<%= form.label :api_key %>
<%= form.text_field :api_key, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
</div>
Expand All @@ -35,7 +35,7 @@
<%= form.fields_for :model_versions, @model_version do |model_version_fields| %>
<div class="my-5">
<%= model_version_fields.label :configuration %>
<%= model_version_fields.text_field :configuration, value: {}.to_json , class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
<%= model_version_fields.text_field :configuration, value: default_configuration(model.executor_type), 'data-model-target' => "configurationField", class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
</div>

<div class="my-5">
Expand Down
7 changes: 7 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,10 @@

en:
hello: "Hello world"
activerecord:
attributes:
model:
executor_types:
llama_cpp: 'Llama.cpp'
openai: 'OpenAI'
ollama: 'Ollama'
2 changes: 1 addition & 1 deletion spec/factories/models.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
factory :model do
name { 'Test Model' }
url { 'http://example.com' }
executor_type { 'base' }
executor_type { 'llama_cpp' }
api_key { 'apikey' }
association :user
end
Expand Down
55 changes: 55 additions & 0 deletions spec/services/executors/llama_cpp_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# frozen_string_literal: true

require 'rails_helper'
require 'webmock/rspec'

RSpec.describe Executors::LlamaCpp do
describe '#call' do
let(:model) { create(:model, url: 'http://example.com/model_endpoint') }
let(:model_version) { create(:model_version, model:, configuration: { param: 'value' }) }
let(:executor) { Executors::Base.new(model_version) }
let(:prompt) { 'Test prompt' }

context 'when HTTP request is successful' do
before do
stub_request(:post, 'http://example.com/model_endpoint')
.with(
body: '{"param":"value","prompt":"Test prompt"}',
headers: {
'Accept' => '*/*',
'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
'Content-Type' => 'application/json',
'User-Agent' => 'Ruby'
}
)
.to_return(status: 200, body: '{"response": "Test response"}', headers: {})
end

it 'returns a completed status with result on successful response' do
expect(executor.call(prompt)).to eq({ status: :completed, result: '{"response": "Test response"}' })
end
end

context 'when HTTP request fails' do
before do
stub_request(:post, 'http://example.com/model_endpoint')
.to_return(status: 500, body: 'Internal Server Error')
end

it 'returns a failed status' do
expect(executor.call(prompt)).to eq({ status: :failed, result: 'Internal Server Error' })
end
end

context 'when HTTP request times out' do
before do
stub_request(:post, 'http://example.com/model_endpoint')
.to_timeout
end

it 'returns a failed status' do
expect(executor.call(prompt)).to eq({ result: { error: { message: 'execution expired' } }.to_json, status: :failed })
end
end
end
end
8 changes: 4 additions & 4 deletions spec/services/model_executor_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@
end
end

context "when executor type is 'base'" do
let(:model) { create(:model, executor_type: 'base') }
context "when executor type is 'llama_cpp'" do
let(:model) { create(:model, executor_type: 'llama_cpp') }
let(:model_version) { create(:model_version, model:) }
let(:prompt) { 'Test prompt' }
let(:executor_instance) { instance_double(Executors::Base) }
let(:executor_instance) { instance_double(Executors::LlamaCpp) }

before do
allow(Executors::Base).to receive(:new).and_return(executor_instance)
allow(Executors::LlamaCpp).to receive(:new).and_return(executor_instance)
allow(executor_instance).to receive(:call).with(prompt).and_return({ result: 'Test result' })
end

Expand Down
Loading