From 5194fabe6245a6e25b405a3c012c144872be73b6 Mon Sep 17 00:00:00 2001 From: Mahmoud Emad Date: Wed, 12 Mar 2025 14:29:40 +0200 Subject: [PATCH 1/3] feat: Add classifier deletion functionality - Added `delete_classifier` function to delete a classifier by ID. - Added corresponding unit tests for the new function. - Updated the client example to demonstrate classifier deletion. - Renamed `jina_client_test.v` to `api_test.v` for better organization. - Renamed `model_embed.v` to `embeddings_api.v` for better organization. - Refactored the embedding API to use enums for task and truncate types, and added error handling for invalid inputs. --- examples/clients/jina.vsh | 7 + .../jina/{jina_client_test.v => api_test.v} | 15 ++ lib/clients/jina/classification_api.v | 20 +- .../jina/{model_embed.v => embeddings_api.v} | 189 ++++++-------- lib/clients/jina/jina_client.v | 245 ------------------ lib/clients/jina/model_rank.v | 104 -------- lib/clients/jina/rank_api.v | 39 +++ 7 files changed, 164 insertions(+), 455 deletions(-) rename lib/clients/jina/{jina_client_test.v => api_test.v} (82%) rename lib/clients/jina/{model_embed.v => embeddings_api.v} (71%) delete mode 100644 lib/clients/jina/jina_client.v delete mode 100644 lib/clients/jina/model_rank.v diff --git a/examples/clients/jina.vsh b/examples/clients/jina.vsh index 7da391d8..7ad98364 100755 --- a/examples/clients/jina.vsh +++ b/examples/clients/jina.vsh @@ -56,5 +56,12 @@ classify_result := jina_client.classify( println('Classification result: ${classify_result}') +// List classifiers classifiers := jina_client.list_classifiers() or { panic('Error fetching classifiers: ${err}') } println('Classifiers: ${classifiers}') + +// Delete classifier +delete_result := jina_client.delete_classifier(classifier_id: classifiers[0].classifier_id) or { + panic('Error deleting classifier: ${err}') +} +println('Delete result: ${delete_result}') diff --git a/lib/clients/jina/jina_client_test.v b/lib/clients/jina/api_test.v similarity index 82% rename from lib/clients/jina/jina_client_test.v rename to lib/clients/jina/api_test.v index 1b453f02..75dfa380 100644 --- a/lib/clients/jina/jina_client_test.v +++ b/lib/clients/jina/api_test.v @@ -85,3 +85,18 @@ fn test_get_classifiers() { classifiers := client.list_classifiers() or { panic('Error fetching classifiers: ${err}') } assert classifiers.len != 0 } + +// Delete classifier +fn test_delete_classifiers() { + time.sleep(1 * time.second) + mut client := setup_client()! + + classifiers := client.list_classifiers() or { panic('Error fetching classifiers: ${err}') } + assert classifiers.len != 0 + + delete_result := client.delete_classifier(classifier_id: classifiers[0].classifier_id) or { + panic('Error deleting classifier: ${err}') + } + + assert delete_result == '{"message":"Classifier ${classifiers[0].classifier_id} deleted"}' +} diff --git a/lib/clients/jina/classification_api.v b/lib/clients/jina/classification_api.v index 820697d6..b06ec13b 100644 --- a/lib/clients/jina/classification_api.v +++ b/lib/clients/jina/classification_api.v @@ -285,7 +285,25 @@ pub fn (mut j Jina) list_classifiers() ![]Classifier { mut httpclient := j.httpclient()! response := httpclient.get(req)! - println('response: ${response}') classifiers := json.decode([]Classifier, response)! return classifiers } + +// ClassifyParams represents parameters for the classification request +@[params] +pub struct DeleteClassifierParams { +pub mut: + classifier_id string @[required] // The ID of the classifier to delete +} + +// Function to delete a classifier by its ID +pub fn (mut j Jina) delete_classifier(params DeleteClassifierParams) !string { + req := httpconnection.Request{ + method: .delete + prefix: 'v1/classifiers/${params.classifier_id}' + } + + mut httpclient := j.httpclient()! + response := httpclient.delete(req)! + return response +} diff --git a/lib/clients/jina/model_embed.v b/lib/clients/jina/embeddings_api.v similarity index 71% rename from lib/clients/jina/model_embed.v rename to lib/clients/jina/embeddings_api.v index f7b564e4..73fb76d9 100644 --- a/lib/clients/jina/model_embed.v +++ b/lib/clients/jina/embeddings_api.v @@ -1,5 +1,6 @@ module jina +import freeflowuniverse.herolib.core.httpconnection import json // JinaModel represents the available Jina models @@ -43,6 +44,32 @@ pub fn jina_model_from_string(s string) !JinaModel { } } +// TruncateType represents the available truncation options +pub enum TruncateType { + none_ // "NONE" + start // "START" + end // "END" +} + +// to_string converts TruncateType enum to its string representation +pub fn (t TruncateType) to_string() string { + return match t { + .none_ { 'NONE' } + .start { 'START' } + .end { 'END' } + } +} + +// from_string converts string to TruncateType enum +pub fn truncate_type_from_string(s string) !TruncateType { + return match s { + 'NONE' { TruncateType.none_ } + 'START' { TruncateType.start } + 'END' { TruncateType.end } + else { error('Invalid truncate type string: ${s}') } + } +} + // EmbeddingType represents the available embedding types pub enum EmbeddingType { float // "float" @@ -81,17 +108,6 @@ pub enum TaskType { separation // "separation" } -// to_string converts TaskType enum to its string representation -pub fn (t TaskType) to_string() string { - return match t { - .retrieval_query { 'retrieval.query' } - .retrieval_passage { 'retrieval.passage' } - .text_matching { 'text-matching' } - .classification { 'classification' } - .separation { 'separation' } - } -} - // from_string converts string to TaskType enum pub fn task_type_from_string(s string) !TaskType { return match s { @@ -104,41 +120,22 @@ pub fn task_type_from_string(s string) !TaskType { } } -// TruncateType represents the available truncation options -pub enum TruncateType { - none_ // "NONE" - start // "START" - end // "END" -} - -// to_string converts TruncateType enum to its string representation -pub fn (t TruncateType) to_string() string { +// to_string converts TaskType enum to its string representation +pub fn (t TaskType) to_string() string { return match t { - .none_ { 'NONE' } - .start { 'START' } - .end { 'END' } + .retrieval_query { 'retrieval.query' } + .retrieval_passage { 'retrieval.passage' } + .text_matching { 'text-matching' } + .classification { 'classification' } + .separation { 'separation' } } } -// from_string converts string to TruncateType enum -pub fn truncate_type_from_string(s string) !TruncateType { - return match s { - 'NONE' { TruncateType.none_ } - 'START' { TruncateType.start } - 'END' { TruncateType.end } - else { error('Invalid truncate type string: ${s}') } - } -} - -// TextEmbeddingInputRaw represents the raw input for text embedding requests as sent to the server -struct TextEmbeddingInputRaw { -mut: - model string = 'jina-embeddings-v2-base-en' - input []string @[required] - task string // Optional: task type as string - type_ string @[json: 'type'] // Optional: embedding type as string - truncate string // Optional: "NONE", "START", "END" - late_chunking bool // Optional: Flag to determine if late chunking is applied +// Usage represents token usage information +pub struct Usage { +pub mut: + total_tokens int + unit string } // TextEmbeddingInput represents the input for text embedding requests with enum types @@ -152,61 +149,14 @@ pub mut: late_chunking ?bool // Flag to determine if late chunking is applied } -// dumps converts TextEmbeddingInput to JSON string -pub fn (t TextEmbeddingInput) dumps() !string { - mut raw := TextEmbeddingInputRaw{ - model: t.model - input: t.input - late_chunking: if v := t.late_chunking { true } else { false } - } - - raw.task = t.task.to_string() - if v := t.type_ { - raw.type_ = v.to_string() - } - - if v := t.truncate { - raw.truncate = v.to_string() - } - - return json.encode(raw) +// EmbeddingData represents a single embedding result +pub struct EmbeddingData { +pub mut: + embedding []f64 + index int + object string } -// from_raw converts TextEmbeddingInputRaw to TextEmbeddingInput -// pub fn loads_text_embedding_input(text string) !TextEmbeddingInput { -// // TODO: go from text to InputObject over json -// // mut input := TextEmbeddingInput{ -// // model: jina_model_from_string(raw.model)? -// // input: raw.input -// // late_chunking: raw.late_chunking -// // } - -// // if raw.task != '' { -// // input.task = task_type_from_string(raw.task)! -// // } - -// // if raw.type_ != '' { -// // input.type_ = embedding_type_from_string(raw.type_)! -// // } - -// // if raw.truncate != '' { -// // input.truncate = truncate_type_from_string(raw.truncate)! -// // } - -// return TextEmbeddingInput{} -// } - -// loads converts a JSON string to TextEmbeddingInput -// pub fn loads(text string) !TextEmbeddingInput { -// // First decode the JSON string to the raw struct -// raw := json.decode(TextEmbeddingInputRaw, text) or { -// return error('Failed to decode JSON: ${err}') -// } - -// // Then convert the raw struct to the typed struct -// return text_embedding_input_from_raw(raw) -// } - // ModelEmbeddingOutput represents the response from embedding requests pub struct ModelEmbeddingOutput { pub mut: @@ -217,17 +167,46 @@ pub mut: dimension int } -// EmbeddingData represents a single embedding result -pub struct EmbeddingData { +// CreateEmbeddingParams represents the parameters for creating embeddings +@[params] +pub struct CreateEmbeddingParams { pub mut: - embedding []f64 - index int - object string + 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 } -// Usage represents token usage information -pub struct Usage { -pub mut: - total_tokens int - unit string +// 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: json.encode(embedding_input) + } + + mut httpclient := j.httpclient()! + response := httpclient.post_json_str(req)! + return json.decode(ModelEmbeddingOutput, response)! } diff --git a/lib/clients/jina/jina_client.v b/lib/clients/jina/jina_client.v deleted file mode 100644 index 5ef4c919..00000000 --- a/lib/clients/jina/jina_client.v +++ /dev/null @@ -1,245 +0,0 @@ -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 -// } diff --git a/lib/clients/jina/model_rank.v b/lib/clients/jina/model_rank.v deleted file mode 100644 index 0dc0e43a..00000000 --- a/lib/clients/jina/model_rank.v +++ /dev/null @@ -1,104 +0,0 @@ -module jina - -import json - -// BulkEmbeddingJobResponse represents the response from bulk embedding operations -pub struct BulkEmbeddingJobResponse { -pub mut: - job_id string - status string - model string - created_at string - completed_at string - error_message string -} - -// DownloadResultResponse represents the response for downloading bulk embedding results -pub struct DownloadResultResponse { -pub mut: - download_url string - expires_at string -} - -// MultiVectorUsage represents token usage information for multi-vector embeddings -pub struct MultiVectorUsage { -pub mut: - total_tokens int -} - -// MultiVectorEmbeddingData represents a single multi-vector embedding result -pub struct MultiVectorEmbeddingData { -pub mut: - embeddings [][]f64 - index int -} - -// ColbertModelEmbeddingsOutput represents the response from multi-vector embedding requests -pub struct ColbertModelEmbeddingsOutput { -pub mut: - model string - object string - data []MultiVectorEmbeddingData - usage MultiVectorUsage -} - -// HTTPValidationError represents a validation error response -pub struct HTTPValidationError { -pub mut: - detail []ValidationError -} - -// ValidationError represents a single validation error -pub struct ValidationError { -pub mut: - loc []string - msg string - type_ string @[json: 'type'] // 'type' is a keyword, so we need to specify the JSON name -} - -// Serialize and deserialize functions for the main request/response types - -// Serialize TextEmbeddingInput to JSON -pub fn (input TextEmbeddingInput) to_json() string { - return json.encode(input) -} - -// Parse JSON to TextEmbeddingInput -pub fn parse_text_embedding_input(json_str string) !TextEmbeddingInput { - return json.decode(TextEmbeddingInput, json_str) -} - -// Parse JSON to ModelEmbeddingOutput -pub fn parse_model_embedding_output(json_str string) !ModelEmbeddingOutput { - return json.decode(ModelEmbeddingOutput, json_str) -} - -// // Serialize RankAPIInput to JSON -// pub fn (input RankAPIInput) to_json() string { -// return json.encode(input) -// } - -// Parse JSON to RankingOutput -pub fn parse_ranking_output(json_str string) !RankingOutput { - return json.decode(RankingOutput, json_str) -} - -// Parse JSON to BulkEmbeddingJobResponse -pub fn parse_bulk_embedding_job_response(json_str string) !BulkEmbeddingJobResponse { - return json.decode(BulkEmbeddingJobResponse, json_str) -} - -// Parse JSON to DownloadResultResponse -pub fn parse_download_result_response(json_str string) !DownloadResultResponse { - return json.decode(DownloadResultResponse, json_str) -} - -// Parse JSON to ColbertModelEmbeddingsOutput -pub fn parse_colbert_model_embeddings_output(json_str string) !ColbertModelEmbeddingsOutput { - return json.decode(ColbertModelEmbeddingsOutput, json_str) -} - -// Parse JSON to HTTPValidationError -pub fn parse_http_validation_error(json_str string) !HTTPValidationError { - return json.decode(HTTPValidationError, json_str) -} diff --git a/lib/clients/jina/rank_api.v b/lib/clients/jina/rank_api.v index e17efd50..6b1dccc2 100644 --- a/lib/clients/jina/rank_api.v +++ b/lib/clients/jina/rank_api.v @@ -1,5 +1,6 @@ module jina +import freeflowuniverse.herolib.core.httpconnection import json pub enum JinaRerankModel { @@ -65,3 +66,41 @@ 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)! +} From a7976c45f998aeffb64c8a831f794192a640f5fe Mon Sep 17 00:00:00 2001 From: Mahmoud Emad Date: Wed, 12 Mar 2025 16:16:37 +0200 Subject: [PATCH 2/3] feat: Add multi-vector API support - Added a new `create_multi_vector` function to the Jina client to support creating multi-vector embeddings. - Added a new `multi_vector_api.v` file containing the implementation for the multi-vector API. - Updated the `jina.vsh` example to demonstrate the usage of the new multi-vector API. --- examples/clients/jina.vsh | 17 +++++ lib/clients/jina/jina_model.v | 1 - lib/clients/jina/multi_vector_api.v | 98 +++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 lib/clients/jina/multi_vector_api.v diff --git a/examples/clients/jina.vsh b/examples/clients/jina.vsh index 7ad98364..7986a567 100755 --- a/examples/clients/jina.vsh +++ b/examples/clients/jina.vsh @@ -65,3 +65,20 @@ delete_result := jina_client.delete_classifier(classifier_id: classifiers[0].cla panic('Error deleting classifier: ${err}') } println('Delete result: ${delete_result}') + +// Create multi vector +multi_vector := jina_client.create_multi_vector( + input: [ + jina.MultiVectorTextDoc{ + text: 'Hello world' + input_type: .document + }, + jina.MultiVectorTextDoc{ + text: "What's up?" + input_type: .query + }, + ] + embedding_type: ['float'] + // dimensions: 96 +)! +println('Multi vector: ${multi_vector}') diff --git a/lib/clients/jina/jina_model.v b/lib/clients/jina/jina_model.v index 8057e768..2d5b6428 100644 --- a/lib/clients/jina/jina_model.v +++ b/lib/clients/jina/jina_model.v @@ -2,7 +2,6 @@ module jina import freeflowuniverse.herolib.data.encoderhero import freeflowuniverse.herolib.core.httpconnection -import net.http import os pub const version = '0.0.0' diff --git a/lib/clients/jina/multi_vector_api.v b/lib/clients/jina/multi_vector_api.v new file mode 100644 index 00000000..8061b757 --- /dev/null +++ b/lib/clients/jina/multi_vector_api.v @@ -0,0 +1,98 @@ +module jina + +import json +import freeflowuniverse.herolib.core.httpconnection + +// Enum for available Jina multi-vector models +pub enum MultiVectorModel { + jina_colbert_v1_en // jina-colbert-v1-en +} + +// Convert the enum to a valid string +pub fn (m MultiVectorModel) to_string() string { + return match m { + .jina_colbert_v1_en { 'jina-colbert-v1-en' } + } +} + +// Enum for input types +pub enum MultiVectorInputType { + document // document + query // query +} + +// MultiVectorTextDoc represents a text document for a multi-vector request +pub struct MultiVectorTextDoc { +pub mut: + id ?string // Optional: ID of the document + text string @[required] // Text of the document + input_type ?MultiVectorInputType // Optional: Type of the embedding to compute, query or document +} + +// MultiVectorRequest represents the JSON request body for the /v1/multi-vector endpoint +struct MultiVectorRequest { + model string // Model name + input []MultiVectorTextDoc // Input documents + embedding_type ?[]string // Optional: Embedding type + dimensions ?int // Optional: Number of dimensions +} + +// MultiVectorResponse represents the JSON response body for the /v1/multi-vector endpoint +pub struct MultiVectorResponse { + data []Embedding // List of embeddings + usage Usage // Usage information + model string // Model name + object string // Object type as string +} + +// EmbeddingObjType represents the embeddings object in the response +pub struct EmbeddingObjType { +pub mut: + float ?[][]f64 // Optional 2D array of floats for multi-vector embeddings + base64 ?[]string // Optional array of base64 strings + binary ?[]u8 // Optional array of bytes +} + +// SEmbeddingType is a sum type to handle different embedding formats +pub type SEmbeddingType = EmbeddingObjType | []f64 | []string | []u8 + +// Embedding represents an embedding vector +pub struct Embedding { + index int // Index of the document + embeddings SEmbeddingType // Embedding vector as a sum type + object string // Object type as string +} + +// MultiVectorParams represents the parameters for a multi-vector request +@[params] +pub struct MultiVectorParams { +pub mut: + model MultiVectorModel = .jina_colbert_v1_en // Model name + input []MultiVectorTextDoc // Input documents + input_type ?MultiVectorInputType // Optional: Type of the embedding to compute, query or document + embedding_type ?[]string // Optional: Embedding type + dimensions ?int // Optional: Number of dimensions +} + +// CreateMultiVector creates a multi-vector request and returns the response +pub fn (mut j Jina) create_multi_vector(params MultiVectorParams) !MultiVectorResponse { + request := MultiVectorRequest{ + model: params.model.to_string() + input: params.input + embedding_type: params.embedding_type + dimensions: params.dimensions + } + + req := httpconnection.Request{ + method: .post + prefix: 'v1/multi-vector' + dataformat: .json + data: json.encode(request) + } + + mut httpclient := j.httpclient()! + response := httpclient.post_json_str(req)! + println('response: ${response}') + result := json.decode(MultiVectorResponse, response)! + return result +} From 4abb46b4bf65ea07a8f48303b613f0fec70e1c6e Mon Sep 17 00:00:00 2001 From: Mahmoud Emad Date: Wed, 12 Mar 2025 16:38:38 +0200 Subject: [PATCH 3/3] feat: Add Jina server health check - Added a health check to the Jina client to verify server availability. - Improved error handling and messaging for failed health checks. - Enhanced client robustness by providing feedback on server status. --- examples/clients/jina.vsh | 2 ++ lib/clients/jina/embeddings_api.v | 28 ++++++++++++++++++++++++++++ lib/clients/jina/jina_factory_.v | 1 - 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/examples/clients/jina.vsh b/examples/clients/jina.vsh index 7986a567..ea430db1 100755 --- a/examples/clients/jina.vsh +++ b/examples/clients/jina.vsh @@ -3,6 +3,8 @@ import freeflowuniverse.herolib.clients.jina mut jina_client := jina.get()! +health := jina_client.health()! +println('Server health: ${health}') // Create embeddings embeddings := jina_client.create_embeddings( diff --git a/lib/clients/jina/embeddings_api.v b/lib/clients/jina/embeddings_api.v index 73fb76d9..8cf61dbe 100644 --- a/lib/clients/jina/embeddings_api.v +++ b/lib/clients/jina/embeddings_api.v @@ -210,3 +210,31 @@ pub fn (mut j Jina) create_embeddings(params CreateEmbeddingParams) !ModelEmbedd response := httpclient.post_json_str(req)! return json.decode(ModelEmbeddingOutput, response)! } + +pub struct HealthResponse { +pub mut: + status string + message string + healthy bool +} + +pub fn (mut j Jina) health() !HealthResponse { + req := httpconnection.Request{ + method: .get + } + + mut httpclient := j.httpclient()! + response := httpclient.send(req)! + if response.code == 200 { + return HealthResponse{ + status: response.code.str() + message: '200 Service available' + healthy: true + } + } + return HealthResponse{ + status: response.code.str() + message: '${response.code} Service Unavailable' + healthy: false + } +} diff --git a/lib/clients/jina/jina_factory_.v b/lib/clients/jina/jina_factory_.v index 6a602a57..e738e5ac 100644 --- a/lib/clients/jina/jina_factory_.v +++ b/lib/clients/jina/jina_factory_.v @@ -2,7 +2,6 @@ module jina import freeflowuniverse.herolib.core.base import freeflowuniverse.herolib.core.playbook -import freeflowuniverse.herolib.ui.console __global ( jina_global map[string]&Jina