Merge branch 'development_actions007' into development_action007_mahmoud
# Conflicts: # lib/clients/jina/jina_client.v # lib/clients/jina/jina_factory_.v # lib/clients/jina/jina_model.v # lib/clients/jina/rank_api.v
This commit is contained in:
245
lib/clients/jina/jina_client.v
Normal file
245
lib/clients/jina/jina_client.v
Normal file
@@ -0,0 +1,245 @@
|
||||
module jina
|
||||
|
||||
import freeflowuniverse.herolib.core.httpconnection
|
||||
// import os
|
||||
import json
|
||||
|
||||
@[params]
|
||||
pub struct CreateEmbeddingParams {
|
||||
pub mut:
|
||||
input []string @[required] // Input texts
|
||||
model JinaModel @[required] // Model name
|
||||
task string @[required] // Task type
|
||||
type_ ?EmbeddingType // embedding type
|
||||
truncate ?TruncateType // truncation type
|
||||
late_chunking ?bool // Flag to determine if late chunking is applied
|
||||
}
|
||||
|
||||
// Create embeddings for input texts
|
||||
pub fn (mut j Jina) create_embeddings(params CreateEmbeddingParams) !ModelEmbeddingOutput {
|
||||
task := task_type_from_string(params.task)!
|
||||
|
||||
mut embedding_input := TextEmbeddingInput{
|
||||
input: params.input
|
||||
model: params.model.to_string()
|
||||
task: task
|
||||
}
|
||||
|
||||
if v := params.type_ {
|
||||
embedding_input.type_ = v
|
||||
}
|
||||
|
||||
if v := params.truncate {
|
||||
embedding_input.truncate = v
|
||||
}
|
||||
|
||||
embedding_input.late_chunking = if _ := params.late_chunking { true } else { false }
|
||||
|
||||
req := httpconnection.Request{
|
||||
method: .post
|
||||
prefix: 'v1/embeddings'
|
||||
dataformat: .json
|
||||
data: embedding_input.to_json()
|
||||
}
|
||||
|
||||
mut httpclient := j.httpclient()!
|
||||
response := httpclient.post_json_str(req)!
|
||||
return parse_model_embedding_output(response)!
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct RerankParams {
|
||||
pub mut:
|
||||
model JinaRerankModel @[required]
|
||||
query string @[required]
|
||||
documents []string @[required]
|
||||
top_n ?int // Optional: Number of top results to return
|
||||
return_documents ?bool // Optional: Flag to determine if the documents should be returned
|
||||
}
|
||||
|
||||
// Rerank documents based on a query
|
||||
pub fn (mut j Jina) rerank(params RerankParams) !RankingOutput {
|
||||
mut rank_input := RerankInput{
|
||||
model: params.model.to_string()
|
||||
query: params.query
|
||||
documents: params.documents
|
||||
}
|
||||
|
||||
if v := params.top_n {
|
||||
rank_input.top_n = v
|
||||
}
|
||||
|
||||
if v := params.return_documents {
|
||||
rank_input.return_documents = v
|
||||
}
|
||||
|
||||
req := httpconnection.Request{
|
||||
method: .post
|
||||
prefix: 'v1/rerank'
|
||||
dataformat: .json
|
||||
data: json.encode(rank_input)
|
||||
}
|
||||
|
||||
mut httpclient := j.httpclient()!
|
||||
response := httpclient.post_json_str(req)!
|
||||
return json.decode(RankingOutput, response)!
|
||||
}
|
||||
|
||||
// // Create embeddings with a TextDoc input
|
||||
// pub fn (mut j Jina) create_embeddings_with_docs(args TextEmbeddingInput) !ModelEmbeddingOutput {
|
||||
|
||||
// req := httpconnection.Request{
|
||||
// method: .post
|
||||
// prefix: 'v1/embeddings'
|
||||
// dataformat: .json
|
||||
// data: json.encode(args)
|
||||
// }
|
||||
|
||||
// response := j.http.get(req)!
|
||||
// return parse_model_embedding_output(response)!
|
||||
// }
|
||||
|
||||
// // Rerank documents based on a query
|
||||
// pub fn (mut j Jina) rerank(query string, documents []string, model string, top_n int) !RankingOutput {
|
||||
// mut rank_input := RankAPIInput{
|
||||
// model: model
|
||||
// query: query
|
||||
// documents: documents
|
||||
// top_n: top_n
|
||||
// }
|
||||
|
||||
// req := httpconnection.Request{
|
||||
// method: .post
|
||||
// prefix: 'v1/rerank'
|
||||
// dataformat: .json
|
||||
// data: rank_input.to_json()
|
||||
// }
|
||||
|
||||
// response := j.http.get(req)!
|
||||
// return parse_ranking_output(response)!
|
||||
// }
|
||||
|
||||
// // Simplified rerank function with default top_n
|
||||
// pub fn (mut j Jina) rerank_simple(query string, documents []string, model string) !RankingOutput {
|
||||
// return j.rerank(query, documents, model, 0)!
|
||||
// }
|
||||
|
||||
// // Classify input texts
|
||||
// pub fn (mut j Jina) classify(input []string, model string, labels []string) !ClassificationOutput {
|
||||
// mut classification_input := ClassificationAPIInput{
|
||||
// model: model
|
||||
// input: input
|
||||
// labels: labels
|
||||
// }
|
||||
|
||||
// req := httpconnection.Request{
|
||||
// method: .post
|
||||
// prefix: 'v1/classify'
|
||||
// dataformat: .json
|
||||
// data: classification_input.to_json()
|
||||
// }
|
||||
|
||||
// response := j.http.get(req)!
|
||||
// return parse_classification_output(response)!
|
||||
// }
|
||||
|
||||
// // Train a classifier
|
||||
// pub fn (mut j Jina) train(examples []TrainingExample, model string, access string) !TrainingOutput {
|
||||
// mut training_input := TrainingAPIInput{
|
||||
// model: model
|
||||
// input: examples
|
||||
// access: access
|
||||
// }
|
||||
|
||||
// req := httpconnection.Request{
|
||||
// method: .post
|
||||
// prefix: 'v1/train'
|
||||
// dataformat: .json
|
||||
// data: training_input.to_json()
|
||||
// }
|
||||
|
||||
// response := j.http.get(req)!
|
||||
// return parse_training_output(response)!
|
||||
// }
|
||||
|
||||
// // List classifiers
|
||||
// pub fn (mut j Jina) list_classifiers() !string {
|
||||
// req := httpconnection.Request{
|
||||
// method: .get
|
||||
// prefix: 'v1/classifiers'
|
||||
// }
|
||||
|
||||
// return j.http.get(req)!
|
||||
// }
|
||||
|
||||
// // Delete a classifier
|
||||
// pub fn (mut j Jina) delete_classifier(classifier_id string) !bool {
|
||||
// req := httpconnection.Request{
|
||||
// method: .delete
|
||||
// prefix: 'v1/classifiers/${classifier_id}'
|
||||
// }
|
||||
|
||||
// j.http.get(req)!
|
||||
// return true
|
||||
// }
|
||||
|
||||
// // Create multi-vector embeddings
|
||||
// pub fn (mut j Jina) create_multi_vector(input []string, model string) !ColbertModelEmbeddingsOutput {
|
||||
// mut data := map[string]json.Any{}
|
||||
// data['model'] = model
|
||||
// data['input'] = input
|
||||
|
||||
// req := httpconnection.Request{
|
||||
// method: .post
|
||||
// prefix: 'v1/multi-embeddings'
|
||||
// dataformat: .json
|
||||
// data: json.encode(data)
|
||||
// }
|
||||
|
||||
// response := j.http.get(req)!
|
||||
// return parse_colbert_model_embeddings_output(response)!
|
||||
// }
|
||||
|
||||
// // Start a bulk embedding job
|
||||
// pub fn (mut j Jina) start_bulk_embedding(file_path string, model string, email string) !BulkEmbeddingJobResponse {
|
||||
// // This endpoint requires multipart/form-data which is not directly supported by the current HTTPConnection
|
||||
// // We need to implement a custom solution for this
|
||||
// return error('Bulk embedding is not implemented yet')
|
||||
// }
|
||||
|
||||
// // Check the status of a bulk embedding job
|
||||
// pub fn (mut j Jina) check_bulk_embedding_status(job_id string) !BulkEmbeddingJobResponse {
|
||||
// req := httpconnection.Request{
|
||||
// method: .get
|
||||
// prefix: 'v1/bulk-embeddings/${job_id}'
|
||||
// }
|
||||
|
||||
// response := j.http.get(req)!
|
||||
// return parse_bulk_embedding_job_response(response)!
|
||||
// }
|
||||
|
||||
// // Download the result of a bulk embedding job
|
||||
// pub fn (mut j Jina) download_bulk_embedding_result(job_id string) !DownloadResultResponse {
|
||||
// req := httpconnection.Request{
|
||||
// method: .post
|
||||
// prefix: 'v1/bulk-embeddings/${job_id}/download-result'
|
||||
// }
|
||||
|
||||
// response := j.http.get(req)!
|
||||
// return parse_download_result_response(response)!
|
||||
// }
|
||||
|
||||
// // Check if the API key is valid by making a simple request
|
||||
// pub fn (mut j Jina) check_auth() !bool {
|
||||
// req := httpconnection.Request{
|
||||
// method: .get
|
||||
// prefix: '/'
|
||||
// }
|
||||
|
||||
// j.http.get(req) or {
|
||||
// return error('Failed to connect to Jina API: ${err}')
|
||||
// }
|
||||
|
||||
// // If we get a response, the API key is valid
|
||||
// return true
|
||||
// }
|
||||
@@ -2,6 +2,7 @@ module jina
|
||||
|
||||
import freeflowuniverse.herolib.core.base
|
||||
import freeflowuniverse.herolib.core.playbook
|
||||
// import freeflowuniverse.herolib.ui.console
|
||||
|
||||
__global (
|
||||
jina_global map[string]&Jina
|
||||
|
||||
@@ -2,6 +2,7 @@ module jina
|
||||
|
||||
import freeflowuniverse.herolib.data.encoderhero
|
||||
import freeflowuniverse.herolib.core.httpconnection
|
||||
// import net.http
|
||||
import os
|
||||
|
||||
pub const version = '0.0.0'
|
||||
|
||||
@@ -16,3 +16,26 @@ data = {
|
||||
|
||||
response = requests.post(url, headers=headers, data=json.dumps(data))
|
||||
print(response.json())
|
||||
|
||||
|
||||
#OTHER EXAMPLE WITH MORE ARGUMENTS
|
||||
|
||||
url = "https://s.jina.ai/"
|
||||
params = {
|
||||
"q": "Jina AI",
|
||||
"gl": "US",
|
||||
"hl": "en",
|
||||
"num": 10,
|
||||
"page": 1,
|
||||
"location": "gent"
|
||||
}
|
||||
headers = {
|
||||
"Accept": "application/json",
|
||||
"Authorization": "Bearer jina_275aefb6495643408d4c499fce548080w5rYjijHfHVBi_vtAqNY6LBk-woz",
|
||||
"X-Return-Format": "markdown",
|
||||
"X-Timeout": "10"
|
||||
}
|
||||
|
||||
response = requests.get(url, params=params, headers=headers)
|
||||
|
||||
print(response.json())
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
module jina
|
||||
|
||||
import freeflowuniverse.herolib.core.httpconnection
|
||||
import json
|
||||
// import json
|
||||
|
||||
pub enum JinaRerankModel {
|
||||
reranker_v2_base_multilingual // 278M
|
||||
@@ -66,41 +65,3 @@ pub fn jina_rerank_model_from_string(s string) !JinaRerankModel {
|
||||
else { error('Invalid JinaRerankModel string: ${s}') }
|
||||
}
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct RerankParams {
|
||||
pub mut:
|
||||
model JinaRerankModel @[required] // Model name
|
||||
query string @[required] // Query text
|
||||
documents []string @[required] // Document texts
|
||||
top_n ?int // Optional: Number of top results to return
|
||||
return_documents ?bool // Optional: Flag to determine if the documents should be returned
|
||||
}
|
||||
|
||||
// Rerank documents based on a query
|
||||
pub fn (mut j Jina) rerank(params RerankParams) !RankingOutput {
|
||||
mut rank_input := RerankInput{
|
||||
model: params.model.to_string()
|
||||
query: params.query
|
||||
documents: params.documents
|
||||
}
|
||||
|
||||
if v := params.top_n {
|
||||
rank_input.top_n = v
|
||||
}
|
||||
|
||||
if v := params.return_documents {
|
||||
rank_input.return_documents = v
|
||||
}
|
||||
|
||||
req := httpconnection.Request{
|
||||
method: .post
|
||||
prefix: 'v1/rerank'
|
||||
dataformat: .json
|
||||
data: json.encode(rank_input)
|
||||
}
|
||||
|
||||
mut httpclient := j.httpclient()!
|
||||
response := httpclient.post_json_str(req)!
|
||||
return json.decode(RankingOutput, response)!
|
||||
}
|
||||
|
||||
8
lib/clients/qdrant/.heroscript
Normal file
8
lib/clients/qdrant/.heroscript
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
!!hero_code.generate_client
|
||||
name:'qdrant'
|
||||
classname:'QDrantClient'
|
||||
singleton:0
|
||||
default:1
|
||||
hasconfig:1
|
||||
reset:0
|
||||
14661
lib/clients/qdrant/openapi.json
Normal file
14661
lib/clients/qdrant/openapi.json
Normal file
File diff suppressed because it is too large
Load Diff
394
lib/clients/qdrant/qdrant_client.v
Normal file
394
lib/clients/qdrant/qdrant_client.v
Normal file
@@ -0,0 +1,394 @@
|
||||
module qdrant
|
||||
|
||||
import freeflowuniverse.herolib.core.httpconnection
|
||||
import json
|
||||
// import os
|
||||
|
||||
// QdrantClient is the main client for interacting with the Qdrant API
|
||||
pub struct QdrantClient {
|
||||
pub mut:
|
||||
name string = 'default'
|
||||
secret string
|
||||
url string = 'http://localhost:6333'
|
||||
}
|
||||
|
||||
// httpclient creates a new HTTP connection to the Qdrant API
|
||||
fn (mut self QdrantClient) httpclient() !&httpconnection.HTTPConnection {
|
||||
mut http_conn := httpconnection.new(
|
||||
name: 'Qdrant_vclient'
|
||||
url: self.url
|
||||
)!
|
||||
|
||||
// Add authentication header if API key is provided
|
||||
if self.secret.len > 0 {
|
||||
http_conn.default_header.add(.api_key, self.secret)
|
||||
}
|
||||
return http_conn
|
||||
}
|
||||
|
||||
// Collections API
|
||||
|
||||
@[params]
|
||||
pub struct CreateCollectionParams {
|
||||
pub mut:
|
||||
collection_name string @[required]
|
||||
vectors VectorsConfig @[required]
|
||||
shard_number ?int
|
||||
replication_factor ?int
|
||||
write_consistency_factor ?int
|
||||
on_disk_payload ?bool
|
||||
hnsw_config ?HnswConfig
|
||||
optimizers_config ?OptimizersConfig
|
||||
wal_config ?WalConfig
|
||||
quantization_config ?QuantizationConfig
|
||||
init_from ?InitFrom
|
||||
timeout ?int
|
||||
}
|
||||
|
||||
// Create a new collection
|
||||
pub fn (mut q QdrantClient) create_collection(params CreateCollectionParams) !bool {
|
||||
mut collection_params := CollectionParams{
|
||||
vectors: params.vectors
|
||||
}
|
||||
|
||||
if v := params.shard_number {
|
||||
collection_params.shard_number = v
|
||||
}
|
||||
|
||||
if v := params.replication_factor {
|
||||
collection_params.replication_factor = v
|
||||
}
|
||||
|
||||
if v := params.write_consistency_factor {
|
||||
collection_params.write_consistency_factor = v
|
||||
}
|
||||
|
||||
if v := params.on_disk_payload {
|
||||
collection_params.on_disk_payload = v
|
||||
}
|
||||
|
||||
if v := params.hnsw_config {
|
||||
collection_params.hnsw_config = v
|
||||
}
|
||||
|
||||
if v := params.optimizers_config {
|
||||
collection_params.optimizers_config = v
|
||||
}
|
||||
|
||||
if v := params.wal_config {
|
||||
collection_params.wal_config = v
|
||||
}
|
||||
|
||||
if v := params.quantization_config {
|
||||
collection_params.quantization_config = v
|
||||
}
|
||||
|
||||
if v := params.init_from {
|
||||
collection_params.init_from = v
|
||||
}
|
||||
|
||||
mut query_params := map[string]string{}
|
||||
if v := params.timeout {
|
||||
query_params['timeout'] = v.str()
|
||||
}
|
||||
|
||||
req := httpconnection.Request{
|
||||
method: .put
|
||||
prefix: 'collections/${params.collection_name}'
|
||||
dataformat: .json
|
||||
data: json.encode(collection_params)
|
||||
params: query_params
|
||||
}
|
||||
|
||||
mut httpclient := q.httpclient()!
|
||||
response := httpclient.send(req)!
|
||||
|
||||
result := json.decode(OperationResponse, response.body)!
|
||||
return result.result
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct ListCollectionsParams {
|
||||
pub mut:
|
||||
timeout ?int
|
||||
}
|
||||
|
||||
// List all collections
|
||||
pub fn (mut q QdrantClient) list_collections(params ListCollectionsParams) !CollectionsResponse {
|
||||
mut query_params := map[string]string{}
|
||||
if v := params.timeout {
|
||||
query_params['timeout'] = v.str()
|
||||
}
|
||||
|
||||
req := httpconnection.Request{
|
||||
method: .get
|
||||
prefix: 'collections'
|
||||
params: query_params
|
||||
}
|
||||
|
||||
mut httpclient := q.httpclient()!
|
||||
response := httpclient.send(req)!
|
||||
|
||||
return json.decode(CollectionsResponse, response.body)!
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct DeleteCollectionParams {
|
||||
pub mut:
|
||||
collection_name string @[required]
|
||||
timeout ?int
|
||||
}
|
||||
|
||||
// Delete a collection
|
||||
pub fn (mut q QdrantClient) delete_collection(params DeleteCollectionParams) !bool {
|
||||
mut query_params := map[string]string{}
|
||||
if v := params.timeout {
|
||||
query_params['timeout'] = v.str()
|
||||
}
|
||||
|
||||
req := httpconnection.Request{
|
||||
method: .delete
|
||||
prefix: 'collections/${params.collection_name}'
|
||||
params: query_params
|
||||
}
|
||||
|
||||
mut httpclient := q.httpclient()!
|
||||
response := httpclient.send(req)!
|
||||
|
||||
result := json.decode(OperationResponse, response.body)!
|
||||
return result.result
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct GetCollectionParams {
|
||||
pub mut:
|
||||
collection_name string @[required]
|
||||
timeout ?int
|
||||
}
|
||||
|
||||
// Get collection info
|
||||
pub fn (mut q QdrantClient) get_collection(params GetCollectionParams) !CollectionInfo {
|
||||
mut query_params := map[string]string{}
|
||||
if v := params.timeout {
|
||||
query_params['timeout'] = v.str()
|
||||
}
|
||||
|
||||
req := httpconnection.Request{
|
||||
method: .get
|
||||
prefix: 'collections/${params.collection_name}'
|
||||
params: query_params
|
||||
}
|
||||
|
||||
mut httpclient := q.httpclient()!
|
||||
response := httpclient.send(req)!
|
||||
|
||||
result := json.decode(CollectionInfoResponse, response.body)!
|
||||
return result.result
|
||||
}
|
||||
|
||||
// Points API
|
||||
|
||||
@[params]
|
||||
pub struct UpsertPointsParams {
|
||||
pub mut:
|
||||
collection_name string @[required]
|
||||
points []PointStruct @[required]
|
||||
wait ?bool
|
||||
ordering ?WriteOrdering
|
||||
}
|
||||
|
||||
// Upsert points
|
||||
pub fn (mut q QdrantClient) upsert_points(params UpsertPointsParams) !PointsOperationResponse {
|
||||
mut query_params := map[string]string{}
|
||||
if v := params.wait {
|
||||
query_params['wait'] = v.str()
|
||||
}
|
||||
|
||||
mut request_body := map[string]json.Any{}
|
||||
request_body['points'] = params.points
|
||||
|
||||
if v := params.ordering {
|
||||
request_body['ordering'] = v
|
||||
}
|
||||
|
||||
req := httpconnection.Request{
|
||||
method: .put
|
||||
prefix: 'collections/${params.collection_name}/points'
|
||||
dataformat: .json
|
||||
data: json.encode(request_body)
|
||||
params: query_params
|
||||
}
|
||||
|
||||
mut httpclient := q.httpclient()!
|
||||
response := httpclient.send(req)!
|
||||
|
||||
return json.decode(PointsOperationResponse, response.body)!
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct DeletePointsParams {
|
||||
pub mut:
|
||||
collection_name string @[required]
|
||||
points_selector PointsSelector @[required]
|
||||
wait ?bool
|
||||
ordering ?WriteOrdering
|
||||
}
|
||||
|
||||
// Delete points
|
||||
pub fn (mut q QdrantClient) delete_points(params DeletePointsParams) !PointsOperationResponse {
|
||||
mut query_params := map[string]string{}
|
||||
if v := params.wait {
|
||||
query_params['wait'] = v.str()
|
||||
}
|
||||
|
||||
mut request_body := map[string]json.Any{}
|
||||
|
||||
if params.points_selector.points != none {
|
||||
request_body['points'] = params.points_selector.points
|
||||
} else if params.points_selector.filter != none {
|
||||
request_body['filter'] = params.points_selector.filter
|
||||
}
|
||||
|
||||
if v := params.ordering {
|
||||
request_body['ordering'] = v
|
||||
}
|
||||
|
||||
req := httpconnection.Request{
|
||||
method: .post
|
||||
prefix: 'collections/${params.collection_name}/points/delete'
|
||||
dataformat: .json
|
||||
data: json.encode(request_body)
|
||||
params: query_params
|
||||
}
|
||||
|
||||
mut httpclient := q.httpclient()!
|
||||
response := httpclient.send(req)!
|
||||
|
||||
return json.decode(PointsOperationResponse, response.body)!
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct GetPointParams {
|
||||
pub mut:
|
||||
collection_name string @[required]
|
||||
id string @[required]
|
||||
with_payload ?WithPayloadSelector
|
||||
with_vector ?WithVector
|
||||
}
|
||||
|
||||
// Get a point by ID
|
||||
pub fn (mut q QdrantClient) get_point(params GetPointParams) !GetPointResponse {
|
||||
mut query_params := map[string]string{}
|
||||
|
||||
if v := params.with_payload {
|
||||
query_params['with_payload'] = json.encode(v)
|
||||
}
|
||||
|
||||
if v := params.with_vector {
|
||||
query_params['with_vector'] = json.encode(v)
|
||||
}
|
||||
|
||||
req := httpconnection.Request{
|
||||
method: .get
|
||||
prefix: 'collections/${params.collection_name}/points/${params.id}'
|
||||
params: query_params
|
||||
}
|
||||
|
||||
mut httpclient := q.httpclient()!
|
||||
response := httpclient.send(req)!
|
||||
|
||||
return json.decode(GetPointResponse, response.body)!
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct SearchParams {
|
||||
pub mut:
|
||||
collection_name string @[required]
|
||||
vector []f32 @[required]
|
||||
limit int = 10
|
||||
filter ?Filter
|
||||
params ?SearchParamsConfig
|
||||
with_payload ?WithPayloadSelector
|
||||
with_vector ?WithVector
|
||||
score_threshold ?f32
|
||||
}
|
||||
|
||||
// Search for points
|
||||
pub fn (mut q QdrantClient) search(params SearchParams) !SearchResponse {
|
||||
// Create a struct to serialize to JSON
|
||||
struct SearchRequest {
|
||||
pub mut:
|
||||
vector []f32
|
||||
limit int
|
||||
filter ?Filter
|
||||
params ?SearchParamsConfig
|
||||
with_payload ?WithPayloadSelector
|
||||
with_vector ?WithVector
|
||||
score_threshold ?f32
|
||||
}
|
||||
|
||||
mut request := SearchRequest{
|
||||
vector: params.vector
|
||||
limit: params.limit
|
||||
}
|
||||
|
||||
if v := params.filter {
|
||||
request.filter = v
|
||||
}
|
||||
|
||||
if v := params.params {
|
||||
request.params = v
|
||||
}
|
||||
|
||||
if v := params.with_payload {
|
||||
request.with_payload = v
|
||||
}
|
||||
|
||||
if v := params.with_vector {
|
||||
request.with_vector = v
|
||||
}
|
||||
|
||||
if v := params.score_threshold {
|
||||
request.score_threshold = v
|
||||
}
|
||||
|
||||
req := httpconnection.Request{
|
||||
method: .post
|
||||
prefix: 'collections/${params.collection_name}/points/search'
|
||||
dataformat: .json
|
||||
data: json.encode(request)
|
||||
}
|
||||
|
||||
mut httpclient := q.httpclient()!
|
||||
response := httpclient.send(req)!
|
||||
|
||||
return json.decode(SearchResponse, response.data)!
|
||||
}
|
||||
|
||||
// Service API
|
||||
|
||||
// Get Qdrant service info
|
||||
pub fn (mut q QdrantClient) get_service_info() !ServiceInfoResponse {
|
||||
req := httpconnection.Request{
|
||||
method: .get
|
||||
prefix: ''
|
||||
}
|
||||
|
||||
mut httpclient := q.httpclient()!
|
||||
response := httpclient.send(req)!
|
||||
|
||||
return json.decode(ServiceInfoResponse, response.data)!
|
||||
}
|
||||
|
||||
// Check Qdrant health
|
||||
pub fn (mut q QdrantClient) health_check() !bool {
|
||||
req := httpconnection.Request{
|
||||
method: .get
|
||||
prefix: 'healthz'
|
||||
}
|
||||
|
||||
mut httpclient := q.httpclient()!
|
||||
response := httpclient.send(req)!
|
||||
|
||||
return response.code == 200
|
||||
}
|
||||
117
lib/clients/qdrant/qdrant_client_test.v
Normal file
117
lib/clients/qdrant/qdrant_client_test.v
Normal file
@@ -0,0 +1,117 @@
|
||||
module qdrant
|
||||
|
||||
fn test_qdrant_client() {
|
||||
mut client := QDrantClient{
|
||||
name: 'test_client'
|
||||
url: 'http://localhost:6333'
|
||||
}
|
||||
|
||||
// Test creating a collection
|
||||
vectors_config := VectorsConfig{
|
||||
size: 128
|
||||
distance: .cosine
|
||||
}
|
||||
|
||||
// Create collection
|
||||
create_result := client.create_collection(
|
||||
collection_name: 'test_collection'
|
||||
vectors: vectors_config
|
||||
) or {
|
||||
assert false, 'Failed to create collection: ${err}'
|
||||
return
|
||||
}
|
||||
assert create_result == true
|
||||
|
||||
// List collections
|
||||
collections := client.list_collections() or {
|
||||
assert false, 'Failed to list collections: ${err}'
|
||||
return
|
||||
}
|
||||
assert 'test_collection' in collections.result
|
||||
|
||||
// Get collection info
|
||||
collection_info := client.get_collection(
|
||||
collection_name: 'test_collection'
|
||||
) or {
|
||||
assert false, 'Failed to get collection info: ${err}'
|
||||
return
|
||||
}
|
||||
assert collection_info.vectors_count == 0
|
||||
|
||||
// Upsert points
|
||||
points := [
|
||||
PointStruct{
|
||||
id: '1'
|
||||
vector: [f32(0.1), 0.2, 0.3, 0.4]
|
||||
payload: {
|
||||
'color': 'red'
|
||||
'category': 'furniture'
|
||||
}
|
||||
},
|
||||
PointStruct{
|
||||
id: '2'
|
||||
vector: [f32(0.2), 0.3, 0.4, 0.5]
|
||||
payload: {
|
||||
'color': 'blue'
|
||||
'category': 'electronics'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
upsert_result := client.upsert_points(
|
||||
collection_name: 'test_collection'
|
||||
points: points
|
||||
wait: true
|
||||
) or {
|
||||
assert false, 'Failed to upsert points: ${err}'
|
||||
return
|
||||
}
|
||||
assert upsert_result.status == 'ok'
|
||||
|
||||
// Search for points
|
||||
search_result := client.search(
|
||||
collection_name: 'test_collection'
|
||||
vector: [f32(0.1), 0.2, 0.3, 0.4]
|
||||
limit: 1
|
||||
) or {
|
||||
assert false, 'Failed to search points: ${err}'
|
||||
return
|
||||
}
|
||||
assert search_result.result.len > 0
|
||||
|
||||
// Get a point
|
||||
point := client.get_point(
|
||||
collection_name: 'test_collection'
|
||||
id: '1'
|
||||
) or {
|
||||
assert false, 'Failed to get point: ${err}'
|
||||
return
|
||||
}
|
||||
if result := point.result {
|
||||
assert result.id == '1'
|
||||
} else {
|
||||
assert false, 'Point not found'
|
||||
}
|
||||
|
||||
// Delete a point
|
||||
delete_result := client.delete_points(
|
||||
collection_name: 'test_collection'
|
||||
points_selector: PointsSelector{
|
||||
points: ['1']
|
||||
}
|
||||
wait: true
|
||||
) or {
|
||||
assert false, 'Failed to delete point: ${err}'
|
||||
return
|
||||
}
|
||||
assert delete_result.status == 'ok'
|
||||
|
||||
// Delete collection
|
||||
delete_collection_result := client.delete_collection(
|
||||
collection_name: 'test_collection'
|
||||
) or {
|
||||
assert false, 'Failed to delete collection: ${err}'
|
||||
return
|
||||
}
|
||||
assert delete_collection_result == true
|
||||
}
|
||||
112
lib/clients/qdrant/qdrant_factory_.v
Normal file
112
lib/clients/qdrant/qdrant_factory_.v
Normal file
@@ -0,0 +1,112 @@
|
||||
module qdrant
|
||||
|
||||
import freeflowuniverse.herolib.core.base
|
||||
import freeflowuniverse.herolib.core.playbook
|
||||
// import freeflowuniverse.herolib.ui.console
|
||||
|
||||
__global (
|
||||
qdrant_global map[string]&QDrantClient
|
||||
qdrant_default string
|
||||
)
|
||||
|
||||
/////////FACTORY
|
||||
|
||||
@[params]
|
||||
pub struct ArgsGet {
|
||||
pub mut:
|
||||
name string
|
||||
}
|
||||
|
||||
fn args_get(args_ ArgsGet) ArgsGet {
|
||||
mut args := args_
|
||||
if args.name == '' {
|
||||
args.name = 'default'
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
pub fn get(args_ ArgsGet) !&QDrantClient {
|
||||
mut context := base.context()!
|
||||
mut args := args_get(args_)
|
||||
mut obj := QDrantClient{}
|
||||
if args.name !in qdrant_global {
|
||||
if !exists(args)! {
|
||||
set(obj)!
|
||||
} else {
|
||||
heroscript := context.hero_config_get('qdrant', args.name)!
|
||||
mut obj_ := heroscript_loads(heroscript)!
|
||||
set_in_mem(obj_)!
|
||||
}
|
||||
}
|
||||
return qdrant_global[args.name] or {
|
||||
println(qdrant_global)
|
||||
// bug if we get here because should be in globals
|
||||
panic('could not get config for qdrant with name, is bug:${args.name}')
|
||||
}
|
||||
}
|
||||
|
||||
// register the config for the future
|
||||
pub fn set(o QDrantClient) ! {
|
||||
set_in_mem(o)!
|
||||
mut context := base.context()!
|
||||
heroscript := heroscript_dumps(o)!
|
||||
context.hero_config_set('qdrant', o.name, heroscript)!
|
||||
}
|
||||
|
||||
// does the config exists?
|
||||
pub fn exists(args_ ArgsGet) !bool {
|
||||
mut context := base.context()!
|
||||
mut args := args_get(args_)
|
||||
return context.hero_config_exists('qdrant', args.name)
|
||||
}
|
||||
|
||||
pub fn delete(args_ ArgsGet) ! {
|
||||
mut args := args_get(args_)
|
||||
mut context := base.context()!
|
||||
context.hero_config_delete('qdrant', args.name)!
|
||||
if args.name in qdrant_global {
|
||||
// del qdrant_global[args.name]
|
||||
}
|
||||
}
|
||||
|
||||
// only sets in mem, does not set as config
|
||||
fn set_in_mem(o QDrantClient) ! {
|
||||
mut o2 := obj_init(o)!
|
||||
qdrant_global[o.name] = &o2
|
||||
qdrant_default = o.name
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct PlayArgs {
|
||||
pub mut:
|
||||
heroscript string // if filled in then plbook will be made out of it
|
||||
plbook ?playbook.PlayBook
|
||||
reset bool
|
||||
}
|
||||
|
||||
pub fn play(args_ PlayArgs) ! {
|
||||
mut args := args_
|
||||
|
||||
mut plbook := args.plbook or { playbook.new(text: args.heroscript)! }
|
||||
|
||||
mut install_actions := plbook.find(filter: 'qdrant.configure')!
|
||||
if install_actions.len > 0 {
|
||||
for install_action in install_actions {
|
||||
heroscript := install_action.heroscript()
|
||||
mut obj2 := heroscript_loads(heroscript)!
|
||||
set(obj2)!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// switch instance to be used for qdrant
|
||||
pub fn switch(name string) {
|
||||
qdrant_default = name
|
||||
}
|
||||
|
||||
// helpers
|
||||
|
||||
@[params]
|
||||
pub struct DefaultConfigArgs {
|
||||
instance string = 'default'
|
||||
}
|
||||
397
lib/clients/qdrant/qdrant_model.v
Normal file
397
lib/clients/qdrant/qdrant_model.v
Normal file
@@ -0,0 +1,397 @@
|
||||
module qdrant
|
||||
|
||||
// import freeflowuniverse.herolib.data.paramsparser
|
||||
import freeflowuniverse.herolib.data.encoderhero
|
||||
// import json
|
||||
// import os
|
||||
|
||||
pub const version = '0.0.0'
|
||||
const singleton = false
|
||||
const default = true
|
||||
|
||||
// THIS THE THE SOURCE OF THE INFORMATION OF THIS FILE, HERE WE HAVE THE CONFIG OBJECT CONFIGURED AND MODELLED
|
||||
|
||||
@[heap]
|
||||
pub struct QDrantClient {
|
||||
pub mut:
|
||||
name string = 'default'
|
||||
secret string
|
||||
url string = 'http://localhost:6333/'
|
||||
}
|
||||
|
||||
// your checking & initialization code if needed
|
||||
fn obj_init(mycfg_ QDrantClient) !QDrantClient {
|
||||
mut mycfg := mycfg_
|
||||
return mycfg
|
||||
}
|
||||
|
||||
/////////////NORMALLY NO NEED TO TOUCH
|
||||
|
||||
pub fn heroscript_dumps(obj QDrantClient) !string {
|
||||
return encoderhero.encode[QDrantClient](obj)!
|
||||
}
|
||||
|
||||
pub fn heroscript_loads(heroscript string) !QDrantClient {
|
||||
mut obj := encoderhero.decode[QDrantClient](heroscript)!
|
||||
return obj
|
||||
}
|
||||
|
||||
// Base response structure
|
||||
pub struct BaseResponse {
|
||||
pub mut:
|
||||
time f32
|
||||
status string
|
||||
}
|
||||
|
||||
// Operation response
|
||||
pub struct OperationResponse {
|
||||
pub mut:
|
||||
time f32
|
||||
status string
|
||||
result bool
|
||||
}
|
||||
|
||||
// Collections response
|
||||
pub struct CollectionsResponse {
|
||||
pub mut:
|
||||
time f32
|
||||
status string
|
||||
result []string
|
||||
}
|
||||
|
||||
// Collection info response
|
||||
pub struct CollectionInfoResponse {
|
||||
pub mut:
|
||||
time f32
|
||||
status string
|
||||
result CollectionInfo
|
||||
}
|
||||
|
||||
// Collection info
|
||||
pub struct CollectionInfo {
|
||||
pub mut:
|
||||
status string
|
||||
optimizer_status OptimizersStatus
|
||||
vectors_count u64
|
||||
indexed_vectors_count ?u64
|
||||
points_count u64
|
||||
segments_count u64
|
||||
config CollectionConfig
|
||||
payload_schema map[string]PayloadIndexInfo
|
||||
}
|
||||
|
||||
// Optimizers status
|
||||
pub struct OptimizersStatus {
|
||||
pub mut:
|
||||
status string
|
||||
}
|
||||
|
||||
// Collection config
|
||||
pub struct CollectionConfig {
|
||||
pub mut:
|
||||
params CollectionParams
|
||||
hnsw_config ?HnswConfig
|
||||
optimizer_config ?OptimizersConfig
|
||||
wal_config ?WalConfig
|
||||
quantization_config ?QuantizationConfig
|
||||
}
|
||||
|
||||
// Collection params
|
||||
pub struct CollectionParams {
|
||||
pub mut:
|
||||
vectors VectorsConfig
|
||||
shard_number ?int
|
||||
replication_factor ?int
|
||||
write_consistency_factor ?int
|
||||
on_disk_payload ?bool
|
||||
hnsw_config ?HnswConfig
|
||||
optimizers_config ?OptimizersConfig
|
||||
wal_config ?WalConfig
|
||||
quantization_config ?QuantizationConfig
|
||||
init_from ?InitFrom
|
||||
}
|
||||
|
||||
// Vectors config
|
||||
pub struct VectorsConfig {
|
||||
pub mut:
|
||||
size int
|
||||
distance Distance
|
||||
hnsw_config ?HnswConfig
|
||||
quantization_config ?QuantizationConfig
|
||||
on_disk ?bool
|
||||
}
|
||||
|
||||
// Distance type
|
||||
pub enum Distance {
|
||||
cosine
|
||||
euclid
|
||||
dot
|
||||
}
|
||||
|
||||
// Convert Distance enum to string
|
||||
pub fn (d Distance) str() string {
|
||||
return match d {
|
||||
.cosine { 'cosine' }
|
||||
.euclid { 'euclid' }
|
||||
.dot { 'dot' }
|
||||
}
|
||||
}
|
||||
|
||||
// HNSW config
|
||||
pub struct HnswConfig {
|
||||
pub mut:
|
||||
m int
|
||||
ef_construct int
|
||||
full_scan_threshold ?int
|
||||
max_indexing_threads ?int
|
||||
on_disk ?bool
|
||||
payload_m ?int
|
||||
}
|
||||
|
||||
// Optimizers config
|
||||
pub struct OptimizersConfig {
|
||||
pub mut:
|
||||
deleted_threshold f32
|
||||
vacuum_min_vector_number int
|
||||
default_segment_number int
|
||||
max_segment_size ?int
|
||||
memmap_threshold ?int
|
||||
indexing_threshold ?int
|
||||
flush_interval_sec ?int
|
||||
max_optimization_threads ?int
|
||||
}
|
||||
|
||||
// WAL config
|
||||
pub struct WalConfig {
|
||||
pub mut:
|
||||
wal_capacity_mb ?int
|
||||
wal_segments_ahead ?int
|
||||
}
|
||||
|
||||
// Quantization config
|
||||
pub struct QuantizationConfig {
|
||||
pub mut:
|
||||
scalar ?ScalarQuantization
|
||||
product ?ProductQuantization
|
||||
binary ?BinaryQuantization
|
||||
}
|
||||
|
||||
// Scalar quantization
|
||||
pub struct ScalarQuantization {
|
||||
pub mut:
|
||||
type_ string
|
||||
quantile ?f32
|
||||
always_ram ?bool
|
||||
}
|
||||
|
||||
// Product quantization
|
||||
pub struct ProductQuantization {
|
||||
pub mut:
|
||||
compression string
|
||||
always_ram ?bool
|
||||
}
|
||||
|
||||
// Binary quantization
|
||||
pub struct BinaryQuantization {
|
||||
pub mut:
|
||||
binary bool
|
||||
always_ram ?bool
|
||||
}
|
||||
|
||||
// Init from
|
||||
pub struct InitFrom {
|
||||
pub mut:
|
||||
collection string
|
||||
shard ?int
|
||||
}
|
||||
|
||||
// Payload index info
|
||||
pub struct PayloadIndexInfo {
|
||||
pub mut:
|
||||
data_type string
|
||||
params ?map[string]string
|
||||
points int
|
||||
}
|
||||
|
||||
// Points operation response
|
||||
pub struct PointsOperationResponse {
|
||||
pub mut:
|
||||
time f32
|
||||
status string
|
||||
result OperationInfo
|
||||
}
|
||||
|
||||
// Operation info
|
||||
pub struct OperationInfo {
|
||||
pub mut:
|
||||
operation_id int
|
||||
status string
|
||||
}
|
||||
|
||||
// Point struct
|
||||
pub struct PointStruct {
|
||||
pub mut:
|
||||
id string
|
||||
vector []f32
|
||||
payload ?map[string]string
|
||||
}
|
||||
|
||||
// Points selector
|
||||
pub struct PointsSelector {
|
||||
pub mut:
|
||||
points ?[]string
|
||||
filter ?Filter
|
||||
}
|
||||
|
||||
// Filter
|
||||
pub struct Filter {
|
||||
pub mut:
|
||||
must ?[]Condition
|
||||
must_not ?[]Condition
|
||||
should ?[]Condition
|
||||
}
|
||||
|
||||
// Filter is serialized directly to JSON
|
||||
|
||||
// Condition interface
|
||||
pub interface Condition {}
|
||||
|
||||
// Field condition
|
||||
pub struct FieldCondition {
|
||||
pub mut:
|
||||
key string
|
||||
match ?string @[json: match]
|
||||
match_integer ?int @[json: match]
|
||||
match_float ?f32 @[json: match]
|
||||
match_bool ?bool @[json: match]
|
||||
range ?Range
|
||||
geo_bounding_box ?GeoBoundingBox
|
||||
geo_radius ?GeoRadius
|
||||
values_count ?ValuesCount
|
||||
}
|
||||
|
||||
// FieldCondition is serialized directly to JSON
|
||||
|
||||
// Range
|
||||
pub struct Range {
|
||||
pub mut:
|
||||
lt ?f32
|
||||
gt ?f32
|
||||
gte ?f32
|
||||
lte ?f32
|
||||
}
|
||||
|
||||
// Range is serialized directly to JSON
|
||||
|
||||
// GeoBoundingBox
|
||||
pub struct GeoBoundingBox {
|
||||
pub mut:
|
||||
top_left GeoPoint
|
||||
bottom_right GeoPoint
|
||||
}
|
||||
|
||||
// GeoBoundingBox is serialized directly to JSON
|
||||
|
||||
// GeoPoint
|
||||
pub struct GeoPoint {
|
||||
pub mut:
|
||||
lon f32
|
||||
lat f32
|
||||
}
|
||||
|
||||
// GeoPoint is serialized directly to JSON
|
||||
|
||||
// GeoRadius
|
||||
pub struct GeoRadius {
|
||||
pub mut:
|
||||
center GeoPoint
|
||||
radius f32
|
||||
}
|
||||
|
||||
// GeoRadius is serialized directly to JSON
|
||||
|
||||
// ValuesCount
|
||||
pub struct ValuesCount {
|
||||
pub mut:
|
||||
lt ?int
|
||||
gt ?int
|
||||
gte ?int
|
||||
lte ?int
|
||||
}
|
||||
|
||||
// ValuesCount is serialized directly to JSON
|
||||
|
||||
// WithPayloadSelector
|
||||
pub struct WithPayloadSelector {
|
||||
pub mut:
|
||||
include ?[]string
|
||||
exclude ?[]string
|
||||
}
|
||||
|
||||
// WithPayloadSelector is serialized directly to JSON
|
||||
|
||||
// WithVector
|
||||
pub struct WithVector {
|
||||
pub mut:
|
||||
include ?[]string
|
||||
}
|
||||
|
||||
// WithVector is serialized directly to JSON
|
||||
|
||||
// Get point response
|
||||
pub struct GetPointResponse {
|
||||
pub mut:
|
||||
time f32
|
||||
status string
|
||||
result ?PointStruct
|
||||
}
|
||||
|
||||
// Search params configuration
|
||||
pub struct SearchParamsConfig {
|
||||
pub mut:
|
||||
hnsw_ef ?int
|
||||
exact ?bool
|
||||
}
|
||||
|
||||
// SearchParamsConfig is serialized directly to JSON
|
||||
|
||||
// Search response
|
||||
pub struct SearchResponse {
|
||||
pub mut:
|
||||
time f32
|
||||
status string
|
||||
result []ScoredPoint
|
||||
}
|
||||
|
||||
// Scored point
|
||||
pub struct ScoredPoint {
|
||||
pub mut:
|
||||
id string
|
||||
version int
|
||||
score f32
|
||||
payload ?map[string]string
|
||||
vector ?[]f32
|
||||
}
|
||||
|
||||
// Write ordering
|
||||
pub struct WriteOrdering {
|
||||
pub mut:
|
||||
type_ string
|
||||
}
|
||||
|
||||
// WriteOrdering is serialized directly to JSON
|
||||
|
||||
// Service info response
|
||||
pub struct ServiceInfoResponse {
|
||||
pub mut:
|
||||
time f32
|
||||
status string
|
||||
result ServiceInfo
|
||||
}
|
||||
|
||||
// Service info
|
||||
pub struct ServiceInfo {
|
||||
pub mut:
|
||||
version string
|
||||
commit ?string
|
||||
}
|
||||
169
lib/clients/qdrant/readme.md
Normal file
169
lib/clients/qdrant/readme.md
Normal file
@@ -0,0 +1,169 @@
|
||||
# Qdrant Client for HeroLib
|
||||
|
||||
This is a V client for [Qdrant](https://qdrant.tech/), a high-performance vector database and similarity search engine.
|
||||
|
||||
## Features
|
||||
|
||||
- Collection management (create, list, delete, get info)
|
||||
- Points management (upsert, delete, search, get)
|
||||
- Service information and health checks
|
||||
- Support for filters, payload management, and vector operations
|
||||
|
||||
## Usage
|
||||
|
||||
### Initialize Client
|
||||
|
||||
```v
|
||||
// Create a new Qdrant client
|
||||
import freeflowuniverse.herolib.clients.qdrant
|
||||
|
||||
mut client := qdrant.get()!
|
||||
|
||||
// Or create with custom configuration
|
||||
mut custom_client := qdrant.QDrantClient{
|
||||
name: 'custom',
|
||||
url: 'http://localhost:6333',
|
||||
secret: 'your_api_key' // Optional
|
||||
}
|
||||
qdrant.set(custom_client)!
|
||||
```
|
||||
|
||||
### Collection Management
|
||||
|
||||
```v
|
||||
// Create a collection
|
||||
vectors_config := qdrant.VectorsConfig{
|
||||
size: 128,
|
||||
distance: .cosine
|
||||
}
|
||||
|
||||
client.create_collection(
|
||||
collection_name: 'my_collection',
|
||||
vectors: vectors_config
|
||||
)!
|
||||
|
||||
// List all collections
|
||||
collections := client.list_collections()!
|
||||
|
||||
// Get collection info
|
||||
collection_info := client.get_collection(
|
||||
collection_name: 'my_collection'
|
||||
)!
|
||||
|
||||
// Delete a collection
|
||||
client.delete_collection(
|
||||
collection_name: 'my_collection'
|
||||
)!
|
||||
```
|
||||
|
||||
### Points Management
|
||||
|
||||
```v
|
||||
// Upsert points
|
||||
points := [
|
||||
qdrant.PointStruct{
|
||||
id: '1',
|
||||
vector: [f32(0.1), 0.2, 0.3, 0.4],
|
||||
payload: {
|
||||
'color': 'red',
|
||||
'category': 'furniture'
|
||||
}
|
||||
},
|
||||
qdrant.PointStruct{
|
||||
id: '2',
|
||||
vector: [f32(0.2), 0.3, 0.4, 0.5],
|
||||
payload: {
|
||||
'color': 'blue',
|
||||
'category': 'electronics'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
client.upsert_points(
|
||||
collection_name: 'my_collection',
|
||||
points: points,
|
||||
wait: true
|
||||
)!
|
||||
|
||||
// Search for points
|
||||
search_result := client.search(
|
||||
collection_name: 'my_collection',
|
||||
vector: [f32(0.1), 0.2, 0.3, 0.4],
|
||||
limit: 10
|
||||
)!
|
||||
|
||||
// Get a point by ID
|
||||
point := client.get_point(
|
||||
collection_name: 'my_collection',
|
||||
id: '1'
|
||||
)!
|
||||
|
||||
// Delete points
|
||||
client.delete_points(
|
||||
collection_name: 'my_collection',
|
||||
points_selector: qdrant.PointsSelector{
|
||||
points: ['1', '2']
|
||||
},
|
||||
wait: true
|
||||
)!
|
||||
```
|
||||
|
||||
### Service Information
|
||||
|
||||
```v
|
||||
// Get service info
|
||||
service_info := client.get_service_info()!
|
||||
|
||||
// Check health
|
||||
is_healthy := client.health_check()!
|
||||
```
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Filtering
|
||||
|
||||
```v
|
||||
// Create a filter
|
||||
filter := qdrant.Filter{
|
||||
must: [
|
||||
qdrant.FieldCondition{
|
||||
key: 'color',
|
||||
match: 'red'
|
||||
},
|
||||
qdrant.FieldCondition{
|
||||
key: 'price',
|
||||
range: qdrant.Range{
|
||||
gte: 10.0,
|
||||
lt: 100.0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// Search with filter
|
||||
search_result := client.search(
|
||||
collection_name: 'my_collection',
|
||||
vector: [f32(0.1), 0.2, 0.3, 0.4],
|
||||
filter: filter,
|
||||
limit: 10
|
||||
)!
|
||||
```
|
||||
|
||||
## Example HeroScript
|
||||
|
||||
```hero
|
||||
!!qdrant.configure
|
||||
name: 'default'
|
||||
secret: 'your_api_key'
|
||||
url: 'http://localhost:6333'
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
Qdrant server can be installed using the provided installer script:
|
||||
|
||||
```bash
|
||||
~/code/github/freeflowuniverse/herolib/examples/installers/db/qdrant.vsh
|
||||
```
|
||||
|
||||
This will install and start a Qdrant server locally.
|
||||
Reference in New Issue
Block a user