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:
2025-03-13 07:14:09 +01:00
47 changed files with 17874 additions and 506 deletions

View 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
// }

View File

@@ -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

View File

@@ -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'

View File

@@ -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())

View File

@@ -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)!
}

View File

@@ -0,0 +1,8 @@
!!hero_code.generate_client
name:'qdrant'
classname:'QDrantClient'
singleton:0
default:1
hasconfig:1
reset:0

File diff suppressed because it is too large Load Diff

View 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
}

View 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
}

View 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'
}

View 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
}

View 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.