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
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
## [Unreleased]

## [0.10.0] - 2026-10-21
- Drop the `faraday` and `graphlient` dependencies in favor of Ruby's built-in `Net::HTTP` (#56)
- Fix `query.get` failing with "interface conversion: interface {} is nil, not int" on Weaviate >= 1.26.4. Only the arguments that are passed are sent in GraphQL queries (#63)
- Add `proxy:` option to `Weaviate::Client.new`. The `http_proxy`/`https_proxy` env vars are respected by default (#55)
- Add `client.collections` (`Weaviate::Collections`) to match Weaviate's terminology. `client.schema` and `Weaviate::Schema` remain as aliases (#69)
- API keys are filtered out of request logs
- Fix `collections.update` failing with 422 on immutable settings: the changes are now merged into the current collection definition, since Weaviate replaces the whole definition on update
- Fix `objects.exists?` raising on missing objects instead of returning `false`
- Fix `objects.batch_create(tenant:)` being ignored by Weaviate: the tenant is now set on each object
- Fix `query.aggs` failing with "groupBy must be a list, instead got: <nil>" when `group_by`/`object_limit` are not passed

### Breaking
- HTTP errors are raised as `Weaviate::HTTPError` subclasses (e.g. `Weaviate::ResourceNotFound`, `Weaviate::ServerError`) instead of `Faraday::Error` subclasses. Network failures raise `Weaviate::ConnectionFailed`/`Weaviate::TimeoutError`
- GraphQL errors are raised as `Weaviate::GraphQLError` (a `Weaviate::Error` subclass) whose message is the joined error messages
- `client.connection` returns a `Weaviate::Connection` and `client.graphql` returns a `Weaviate::GraphQL` instead of Faraday/Graphlient clients
- The `adapter:` option is deprecated and ignored

## [0.9.2] - 2024-10-01
- Weaviate::Client constructor accepts customer logger: to be passed in

Expand Down
4 changes: 3 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ gem "rake", "~> 13.0"

gem "rspec", "~> 3.0"
gem "standard", "~> 1.25.0"
gem "graphql-client", "~> 0.19.0"
gem "racc" # required by standard, previously pulled in through graphlient

gem "ostruct" # used in specs, no longer a default gem on Ruby >= 3.5
49 changes: 9 additions & 40 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,52 +1,21 @@
PATH
remote: .
specs:
weaviate-ruby (0.9.2)
faraday (>= 2.0.1, < 3.0)
graphlient (>= 0.7.0, < 0.9.0)
weaviate-ruby (0.10.0)
logger

GEM
remote: https://rubygems.org/
specs:
activesupport (7.1.3)
base64
bigdecimal
concurrent-ruby (~> 1.0, >= 1.0.2)
connection_pool (>= 2.2.5)
drb
i18n (>= 1.6, < 2)
minitest (>= 5.1)
mutex_m
tzinfo (~> 2.0)
ast (2.4.2)
base64 (0.2.0)
bigdecimal (3.1.6)
byebug (11.1.3)
coderay (1.1.3)
concurrent-ruby (1.2.3)
connection_pool (2.4.1)
diff-lcs (1.5.0)
drb (2.2.0)
ruby2_keywords
faraday (2.7.10)
faraday-net_http (>= 2.0, < 3.1)
ruby2_keywords (>= 0.0.4)
faraday-net_http (3.0.2)
graphlient (0.7.0)
faraday (~> 2.0)
graphql-client
graphql (2.2.6)
racc (~> 1.4)
graphql-client (0.19.0)
activesupport (>= 3.0)
graphql
i18n (1.14.1)
concurrent-ruby (~> 1.0)
json (2.6.3)
language_server-protocol (3.17.0.3)
logger (1.7.0)
method_source (1.0.0)
minitest (5.21.2)
mutex_m (0.2.0)
ostruct (0.6.3)
parallel (1.22.1)
parser (3.2.1.1)
ast (~> 2.4.1)
Expand All @@ -56,7 +25,7 @@ GEM
pry-byebug (3.10.1)
byebug (~> 11.0)
pry (>= 0.13, < 0.15)
racc (1.7.3)
racc (1.8.1)
rainbow (3.1.1)
rake (13.0.6)
rdiscount (2.2.7.4)
Expand Down Expand Up @@ -91,26 +60,26 @@ GEM
rubocop (>= 1.7.0, < 2.0)
rubocop-ast (>= 0.4.0)
ruby-progressbar (1.13.0)
ruby2_keywords (0.0.5)
standard (1.25.3)
language_server-protocol (~> 3.17.0.2)
rubocop (~> 1.48.1)
rubocop-performance (~> 1.16.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unicode-display_width (2.4.2)
yard (0.9.37)

PLATFORMS
arm64-darwin-21
arm64-darwin-23
arm64-darwin-24
ruby
x86_64-darwin-19
x86_64-darwin-21
x86_64-linux

DEPENDENCIES
graphql-client (~> 0.19.0)
ostruct
pry-byebug (~> 3.9)
racc
rake (~> 13.0)
rdiscount
rspec (~> 3.0)
Expand Down
63 changes: 42 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,27 @@ client = Weaviate::Client.new(
url: 'https://some-endpoint.weaviate.network', # Replace with your endpoint
api_key: '', # Weaviate API key
model_service: :openai, # Service that will be used to generate vectors. Possible values: :openai, :azure_openai, :cohere, :huggingface, :google_palm
model_service_api_key: 'xxxxxxx' # Either OpenAI, Azure OpenAI, Cohere, Hugging Face or Google PaLM api key
model_service_api_key: 'xxxxxxx', # Either OpenAI, Azure OpenAI, Cohere, Hugging Face or Google PaLM api key
proxy: 'http://user:pass@localhost:8888', # Optional. Defaults to the http_proxy/https_proxy env vars
logger: Logger.new($stdout) # Optional. Requests and responses are logged with API keys filtered out
)
```

### Using the Schema endpoints
### Errors

All errors inherit from `Weaviate::Error`:

- `Weaviate::HTTPError` - Weaviate responded with a 4xx/5xx status (`#status` and `#body` are available). Subclasses: `Weaviate::BadRequestError`, `Weaviate::UnauthorizedError`, `Weaviate::ForbiddenError`, `Weaviate::ResourceNotFound`, `Weaviate::ConflictError`, `Weaviate::UnprocessableEntityError`, `Weaviate::ServerError`
- `Weaviate::GraphQLError` - a GraphQL query returned errors (`#errors` is available)
- `Weaviate::ConnectionFailed` / `Weaviate::TimeoutError` - Weaviate could not be reached

### Using the Collections endpoints

`client.collections` was previously called `client.schema`, which still works as an alias.

```ruby
# Creating a new data object class in the schema
client.schema.create(
# Creating a new collection
client.collections.create(
class_name: 'Question',
description: 'Information from a Jeopardy! question',
properties: [
Expand All @@ -70,36 +82,36 @@ client.schema.create(
vectorizer: "text2vec-openai"
)

# Get a single class from the schema
client.schema.get(class_name: 'Question')
# Get a single collection
client.collections.get(class_name: 'Question')

# Get the schema
client.schema.list()
# List all collections
client.collections.list()

# Update settings of an existing schema class.
# Update settings of an existing collection.
# Does not support modifying existing properties.
client.schema.update(
client.collections.update(
class_name: 'Question',
description: 'Information from a Wheel of Fortune question'
)

# Adding a new property
client.schema.add_property(
client.collections.add_property(
class_name: 'Question',
property: {
"dataType": ["boolean"],
"name": "homepage"
}
)

# Inspect the shards of a class
client.schema.shards(class_name: 'Question')
# Inspect the shards of a collection
client.collections.shards(class_name: 'Question')

# Remove a class (and all data in the instances) from the schema.
client.schema.delete(class_name: 'Question')
# Remove a collection (and all data in the instances).
client.collections.delete(class_name: 'Question')

# Creating a new data object class in the schema while configuring the vectorizer on the schema and on individual properties (Ollama example)
client.schema.create(
# Creating a new collection while configuring the vectorizer on the collection and on individual properties (Ollama example)
client.collections.create(
class_name: 'Question',
description: 'Information from a Jeopardy! question',
properties: [
Expand Down Expand Up @@ -140,9 +152,9 @@ client.schema.create(
},
)

# Creating named schemas
# Creating collections with named vectors

client.schema.create(
client.collections.create(
class_name: 'ArticleNV',
description: 'Articles with named vectors',
properties: [
Expand Down Expand Up @@ -306,6 +318,15 @@ client.query.get class_name: 'Question', where: '{ operator: Like, valueText: "S

client.query.get class_name: 'Question', fields: 'answer question category _additional { id }', after: "3c5f7039-37f3-4244-b3e2-8f4a083e448d", limit: "1"

# Limiting results by distance or certainty: pass `distance` or `certainty` along with `near_text`, `near_vector`
# or `near_object`, and request them back via `_additional`.
client.query.get(
class_name: 'Question',
fields: 'question _additional { distance certainty }',
near_text: '{ concepts: ["biology"], distance: 0.6 }', # or certainty: 0.7
limit: 5
)

# Named vector query - uses targetVectors
query_title = client.query.get(
class_name: 'ArticleNV',
Expand Down Expand Up @@ -403,10 +424,10 @@ client.ready?

### Tenants

Any schema can be multi-tenant by passing in these options for to `schema.create()`.
Any collection can be multi-tenant by passing in these options to `collections.create()`.

```ruby
client.schema.create(
client.collections.create(
# Other keys...
multi_tenant: true,
auto_tenant_creation: true,
Expand Down
7 changes: 5 additions & 2 deletions lib/weaviate.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
# frozen_string_literal: true

require_relative "weaviate/version"
require_relative "weaviate/error"

module Weaviate
autoload :Base, "weaviate/base"
autoload :Client, "weaviate/client"
autoload :Error, "weaviate/error"
autoload :Schema, "weaviate/schema"
autoload :Connection, "weaviate/connection"
autoload :Collections, "weaviate/collections"
autoload :Schema, "weaviate/collections"
autoload :GraphQL, "weaviate/graphql"
autoload :Meta, "weaviate/meta"
autoload :Objects, "weaviate/objects"
autoload :OIDC, "weaviate/oidc"
Expand Down
71 changes: 34 additions & 37 deletions lib/weaviate/client.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
# frozen_string_literal: true

require "faraday"
require "graphlient"
require "logger"

module Weaviate
class Client
attr_reader :url, :api_key, :model_service, :model_service_api_key, :adapter, :logger
attr_reader :url, :api_key, :model_service, :model_service_api_key, :proxy, :logger

API_VERSION = "v1"

Expand All @@ -18,30 +17,44 @@ class Client
google_palm: "X-Palm-Api-Key"
}

# @param url [String] The Weaviate instance URL
# @param api_key [String] The Weaviate API key
# @param model_service [Symbol] The service used to generate vectors, one of the API_KEY_HEADERS keys
# @param model_service_api_key [String] The API key for the model service
# @param proxy [String] Proxy URL, e.g. "http://user:pass@localhost:8888". Defaults to the http_proxy/https_proxy env vars.
# @param logger [Logger] Logger for requests and responses
# @param adapter [Object] Deprecated and ignored, Faraday is no longer used
def initialize(
url:,
api_key: nil,
model_service: nil,
model_service_api_key: nil,
adapter: Faraday.default_adapter,
logger: nil
proxy: nil,
logger: nil,
adapter: nil
)
validate_model_service!(model_service) unless model_service.nil?
warn "[weaviate-ruby] The `adapter:` option is deprecated and ignored, Faraday is no longer used." unless adapter.nil?

@url = url
@api_key = api_key
@model_service = model_service
@model_service_api_key = model_service_api_key
@adapter = adapter
@proxy = proxy
@logger = logger || Logger.new($stdout)
end

def oidc
Weaviate::OIDC.new(client: self).get
end

def collections
@collections ||= Weaviate::Collections.new(client: self)
end

# @deprecated Weaviate now refers to schema classes as collections. Use {#collections} instead.
def schema
@schema ||= Weaviate::Schema.new(client: self)
collections
end

def meta
Expand Down Expand Up @@ -81,43 +94,27 @@ def query
end

def graphql
headers = {}

if model_service && model_service_api_key
headers[API_KEY_HEADERS[model_service]] = model_service_api_key
end

if api_key
headers["Authorization"] = "Bearer #{api_key}"
end

@graphql ||= Graphlient::Client.new(
"#{url}/#{API_VERSION}/graphql",
headers: headers,
http_options: {
read_timeout: 20,
write_timeout: 30
}
)
@graphql ||= Weaviate::GraphQL.new(client: self)
end

def connection
@connection ||= Faraday.new(url: "#{url}/#{API_VERSION}/") do |faraday|
if api_key
faraday.request :authorization, :Bearer, api_key
end
faraday.request :json
faraday.response :logger, logger, {headers: true, bodies: true, errors: true}
faraday.response :json, content_type: /\bjson$/
faraday.response :raise_error
faraday.adapter adapter

faraday.headers[API_KEY_HEADERS[model_service]] = model_service_api_key if model_service && model_service_api_key
end
@connection ||= Weaviate::Connection.new(
url: "#{url}/#{API_VERSION}",
headers: headers,
logger: logger,
proxy: proxy
)
end

private

def headers
headers = {}
headers["Authorization"] = "Bearer #{api_key}" if api_key
headers[API_KEY_HEADERS[model_service]] = model_service_api_key if model_service && model_service_api_key
headers
end

def validate_model_service!(model_service)
unless API_KEY_HEADERS.key?(model_service)
raise ArgumentError, "Invalid model service: #{model_service}. Acceptable values are: #{API_KEY_HEADERS.keys.join(", ")}"
Expand Down
Loading
Loading