Skip to content
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/.bundle/
/vendor/
/.yardoc
/_yardoc/
/coverage/
Expand Down
2 changes: 2 additions & 0 deletions lib/typesense.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,5 @@ module Typesense
require_relative 'typesense/stemming'
require_relative 'typesense/stemming_dictionaries'
require_relative 'typesense/stemming_dictionary'
require_relative 'typesense/nl_search_models'
require_relative 'typesense/nl_search_model'
2 changes: 1 addition & 1 deletion lib/typesense/analytics_rules.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def [](rule_name)
private

def endpoint_path(operation = nil)
"#{AnalyticsRules::RESOURCE_PATH}#{operation.nil? ? '' : "/#{URI.encode_www_form_component(operation)}"}"
"#{AnalyticsRules::RESOURCE_PATH}#{"/#{URI.encode_www_form_component(operation)}" unless operation.nil?}"
end
end
end
3 changes: 2 additions & 1 deletion lib/typesense/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
module Typesense
class Client
attr_reader :configuration, :collections, :aliases, :keys, :debug, :health, :metrics, :stats, :operations,
:multi_search, :analytics, :presets, :stemming
:multi_search, :analytics, :presets, :stemming, :nl_search_models

def initialize(options = {})
@configuration = Configuration.new(options)
Expand All @@ -20,6 +20,7 @@ def initialize(options = {})
@analytics = Analytics.new(@api_call)
@stemming = Stemming.new(@api_call)
@presets = Presets.new(@api_call)
@nl_search_models = NlSearchModels.new(@api_call)
end
end
end
2 changes: 1 addition & 1 deletion lib/typesense/documents.rb
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ def truncate
private

def endpoint_path(operation = nil)
"#{Collections::RESOURCE_PATH}/#{URI.encode_www_form_component(@collection_name)}#{Documents::RESOURCE_PATH}#{operation.nil? ? '' : "/#{operation}"}"
"#{Collections::RESOURCE_PATH}/#{URI.encode_www_form_component(@collection_name)}#{Documents::RESOURCE_PATH}#{"/#{operation}" unless operation.nil?}"
end
end
end
28 changes: 28 additions & 0 deletions lib/typesense/nl_search_model.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# frozen_string_literal: true

module Typesense
class NlSearchModel
def initialize(model_id, api_call)
@model_id = model_id
@api_call = api_call
end

def retrieve
@api_call.get(endpoint_path)
end

def update(update_schema)
@api_call.put(endpoint_path, update_schema)
end

def delete
@api_call.delete(endpoint_path)
end

private

def endpoint_path
"#{NlSearchModels::RESOURCE_PATH}/#{URI.encode_www_form_component(@model_id)}"
end
end
end
24 changes: 24 additions & 0 deletions lib/typesense/nl_search_models.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# frozen_string_literal: true

module Typesense
class NlSearchModels
RESOURCE_PATH = '/nl_search_models'

def initialize(api_call)
@api_call = api_call
@nl_search_models = {}
end

def create(schema)
@api_call.post(RESOURCE_PATH, schema)
end

def retrieve
@api_call.get(RESOURCE_PATH)
end

def [](model_id)
@nl_search_models[model_id] ||= NlSearchModel.new(model_id, @api_call)
end
end
end
2 changes: 1 addition & 1 deletion lib/typesense/overrides.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def [](override_id)
private

def endpoint_path(operation = nil)
"#{Collections::RESOURCE_PATH}/#{URI.encode_www_form_component(@collection_name)}#{Overrides::RESOURCE_PATH}#{operation.nil? ? '' : "/#{URI.encode_www_form_component(operation)}"}"
"#{Collections::RESOURCE_PATH}/#{URI.encode_www_form_component(@collection_name)}#{Overrides::RESOURCE_PATH}#{"/#{URI.encode_www_form_component(operation)}" unless operation.nil?}"
end
end
end
2 changes: 1 addition & 1 deletion lib/typesense/presets.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def [](preset_name)
private

def endpoint_path(operation = nil)
"#{Presets::RESOURCE_PATH}#{operation.nil? ? '' : "/#{URI.encode_www_form_component(operation)}"}"
"#{Presets::RESOURCE_PATH}#{"/#{URI.encode_www_form_component(operation)}" unless operation.nil?}"
end
end
end
2 changes: 1 addition & 1 deletion lib/typesense/stemming_dictionaries.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def [](dict_id)
private

def endpoint_path(operation = nil)
"#{StemmingDictionaries::RESOURCE_PATH}#{operation.nil? ? '' : "/#{URI.encode_www_form_component(operation)}"}"
"#{StemmingDictionaries::RESOURCE_PATH}#{"/#{URI.encode_www_form_component(operation)}" unless operation.nil?}"
end
end
end
2 changes: 1 addition & 1 deletion lib/typesense/synonyms.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def [](synonym_id)
private

def endpoint_path(operation = nil)
"#{Collections::RESOURCE_PATH}/#{URI.encode_www_form_component(@collection_name)}#{Synonyms::RESOURCE_PATH}#{operation.nil? ? '' : "/#{URI.encode_www_form_component(operation)}"}"
"#{Collections::RESOURCE_PATH}/#{URI.encode_www_form_component(@collection_name)}#{Synonyms::RESOURCE_PATH}#{"/#{URI.encode_www_form_component(operation)}" unless operation.nil?}"
end
end
end
117 changes: 117 additions & 0 deletions spec/typesense/nl_search_models_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# frozen_string_literal: true

require_relative '../spec_helper'

describe 'NlSearchModels', :integration do
# These tests require external API access and should not run on CI by default
next unless ENV['OPENAI_API_KEY']

let(:client) do
Typesense::Client.new(
nodes: [{ host: 'localhost', port: '8108', protocol: 'http' }],
api_key: 'xyz',
connection_timeout_seconds: 10
)
end

def create_model_schema(id_suffix = nil)
model_id = id_suffix ? "test_openai_model_#{id_suffix}" : "test_openai_model_#{Time.now.to_i}_#{rand(1000)}"
{
'id' => model_id,
'model_name' => 'openai/gpt-4.1',
'api_key' => ENV.fetch('OPENAI_API_KEY', nil),
'max_bytes' => 16_000,
'temperature' => 0.0
}
end

def cleanup_model(model_id)
client.nl_search_models[model_id].delete
rescue Typesense::Error::ObjectNotFound
# Model doesn't exist, that's fine
end

before do
WebMock.disable!
end

after do
WebMock.enable!
end

it 'can create a nl search model' do
model_schema = create_model_schema('create_test')

begin
response = client.nl_search_models.create(model_schema)
expect(response['id']).to eq(model_schema['id'])
expect(response['model_name']).to eq('openai/gpt-4.1')
expect(response['max_bytes']).to eq(16_000)
expect(response['temperature']).to eq(0.0)
ensure
cleanup_model(model_schema['id'])
end
end

it 'can retrieve a specific nl search model' do
model_schema = create_model_schema('retrieve_test')

begin
client.nl_search_models.create(model_schema)
response = client.nl_search_models[model_schema['id']].retrieve
expect(response['id']).to eq(model_schema['id'])
expect(response['model_name']).to eq('openai/gpt-4.1')
ensure
cleanup_model(model_schema['id'])
end
end

it 'can retrieve all nl search models' do
model_schema = create_model_schema('list_test')

begin
client.nl_search_models.create(model_schema)

response = client.nl_search_models.retrieve
expect(response).to be_an(Array)
expect(response.length).to be >= 1

model_ids = response.map { |model| model['id'] }
expect(model_ids).to include(model_schema['id'])
ensure
cleanup_model(model_schema['id'])
end
end

it 'can update a nl search model' do
model_schema = create_model_schema('update_test')

begin
client.nl_search_models.create(model_schema)

update_schema = {
'temperature' => 0.5,
'system_prompt' => 'Updated system prompt for electronics search'
}

response = client.nl_search_models[model_schema['id']].update(update_schema)
expect(response['temperature']).to eq(0.5)
expect(response['system_prompt']).to eq('Updated system prompt for electronics search')
ensure
cleanup_model(model_schema['id'])
end
end

it 'can delete a nl search model' do
model_schema = create_model_schema('delete_test')

client.nl_search_models.create(model_schema)

response = client.nl_search_models[model_schema['id']].delete
expect(response['id']).to eq(model_schema['id'])

expect do
client.nl_search_models[model_schema['id']].retrieve
end.to raise_error(Typesense::Error::ObjectNotFound)
end
end