Skip to content

feat/ext-api-keys#7809

Merged
dancixx merged 13 commits intodevfrom
feat/ext-api-keys
Feb 2, 2026
Merged

feat/ext-api-keys#7809
dancixx merged 13 commits intodevfrom
feat/ext-api-keys

Conversation

@dancixx
Copy link
Contributor

@dancixx dancixx commented Dec 19, 2025

All Submissions:

  • Contributions should target the dev branch. Did you create your branch from dev?
  • Have you followed the guidelines in our Contributing document?
  • Have you checked to ensure there aren't other open Pull Requests for the same update/change?

New Feature Submissions:

  1. Does your submission pass tests?
  2. Have you formatted your code locally using cargo +nightly fmt --all command prior to submission?
  3. Have you checked your code using cargo clippy --workspace --all-features command?

Changes to Core Features:

  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you written new tests for your core changes, as applicable?
  • Have you successfully ran tests with your changes locally?

Before

PUT /collections/named4/points?wait=true
{
  "points": [{
    "id": 1,
    "vector": {
      "large": {
        "text": "https://picsum.photos/seed/1/3840/2160",
        "model": "openai/text-embedding-3-small",
        "options": {
          "openai-api-key": "sk-proj-..."
        }
      },
      "tiny": {
        "text": "https://picsum.photos/seed/1/3840/2160",
        "model": "cohere/embed-v4.0",
        "options": {
          "cohere-api-key": "cYZLT...",
          "mrl": 64
        }
      }
    }
  }]
}

After

PUT /collections/named4/points?wait=true
Headers:
  openai-api-key: sk-proj-...
  cohere-api-key: cYZLT...

Body:
{
  "points": [{
    "id": 1,
    "vector": {
      "large": {
        "text": "https://picsum.photos/seed/1/3840/2160",
        "model": "openai/text-embedding-3-small",
        "options": {}
      },
      "tiny": {
        "text": "https://picsum.photos/seed/1/3840/2160",
        "model": "cohere/embed-v4.0",
        "options": {
          "mrl": 64
        }
      }
    }
  }]
}

@dancixx dancixx changed the base branch from master to dev December 19, 2025 12:56
@dancixx dancixx changed the title Feat/ext api keys feat/ext-api-keys Dec 19, 2025
@dancixx dancixx marked this pull request as draft December 19, 2025 12:56
@qdrant qdrant deleted a comment from coderabbitai bot Dec 19, 2025
@dancixx dancixx marked this pull request as ready for review January 20, 2026 10:30
@dancixx dancixx requested a review from generall January 20, 2026 10:30
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 20, 2026

📝 Walkthrough

Walkthrough

Refactors inference authentication to support external API keys alongside the existing token. Adds a new InferenceApiKeys module and type that captures a token plus provider-specific API keys extracted from HTTP and gRPC requests. Replaces InferenceToken uses with InferenceApiKeys across Actix and Tonic handlers, updates InferenceParams to hold api_keys, and adjusts the service layer to conditionally attach API-key-derived headers to outbound requests. Removes the old token module and preserves legacy token compatibility within the new module.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Possibly related PRs

  • fix/inference timeout error #7598: Makes similar changes to inference-auth plumbing, replacing per-request token usage with a structured auth type and updating handlers and common inference modules.

Suggested labels

release:1.16.1

Suggested reviewers

  • timvisee
🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'feat/ext-api-keys' is vague and overly generic, using abbreviated terminology that does not clearly convey what external API keys feature does or why it matters. Replace with a more descriptive title such as 'Extract API keys from request headers for embedding providers' or similar that clarifies the feature intent.
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The description is clearly related to the changeset, detailing the feature to extract external API keys from requests and forward them via headers instead of embedding them in vector options.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/ext-api-keys

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/actix/api/update_api.rs (1)

331-360: update_batch is missing API key extraction.

The update_batch handler does not extract or propagate ApiKeys, unlike upsert_points and update_vectors in this file, and unlike the gRPC update_batch handler in src/tonic/api/points_api.rs (lines 290-292) which does include API key extraction. Since batch operations can include upsert operations that may require external API keys for inference, this appears to be an oversight.

Suggested fix
 #[post("/collections/{name}/points/batch")]
+#[allow(clippy::too_many_arguments)]
 async fn update_batch(
     dispatcher: web::Data<Dispatcher>,
     collection: Path<CollectionPath>,
     operations: Json<UpdateOperations>,
     params: Query<UpdateParams>,
     service_config: web::Data<ServiceConfig>,
     ActixAccess(access): ActixAccess,
+    api_keys: ApiKeys,
     inference_token: InferenceToken,
 ) -> impl Responder {
     let operations = operations.into_inner();

     let request_hw_counter = get_request_hardware_counter(
         &dispatcher,
         collection.name.clone(),
         service_config.hardware_reporting(),
         Some(params.wait),
     );

-    let inference_params = InferenceParams::new(inference_token.clone(), params.timeout);
+    let inference_params =
+        InferenceParams::new(inference_token, params.timeout).with_ext_api_keys(api_keys);
     let timing = Instant::now();
src/tonic/api/points_api.rs (1)

668-670: Add API key extraction to query_groups handler.

The query_groups handler at lines 668-670 is missing the external API key extraction that both query and query_batch handlers include. Add:

let api_keys = extract_api_key(request.metadata());

and chain it to InferenceParams:

let inference_params = InferenceParams::new(inference_token, timeout).with_ext_api_keys(api_keys);
src/actix/api/query_api.rs (1)

109-132: Inconsistent API keys support across query endpoints.

query_points_batch does not extract or forward API keys, while query_points does. If external API keys are needed for inference during query operations, this endpoint should also support them for consistency.

The same issue applies to query_points_groups (lines 196-221).

Suggested fix for query_points_batch
 #[post("/collections/{name}/points/query/batch")]
+#[allow(clippy::too_many_arguments)]
 async fn query_points_batch(
     dispatcher: web::Data<Dispatcher>,
     collection: Path<CollectionPath>,
     request: Json<QueryRequestBatch>,
     params: Query<ReadParams>,
     service_config: web::Data<ServiceConfig>,
     ActixAccess(access): ActixAccess,
+    api_keys: ApiKeys,
     inference_token: InferenceToken,
 ) -> impl Responder {
     ...
-    let inference_params = InferenceParams::new(inference_token, params.timeout());
+    let inference_params =
+        InferenceParams::new(inference_token, params.timeout()).with_ext_api_keys(api_keys);
🤖 Fix all issues with AI agents
In `@src/common/inference/ext_api_keys.rs`:
- Around line 56-67: The current impl From<ApiKeys> for
reqwest::header::HeaderMap panics on invalid header names/values due to expect()
in the from(api_keys: ApiKeys) method; change this to a fallible conversion by
implementing TryFrom<ApiKeys> for reqwest::header::HeaderMap (or add a new
ApiKeys::to_header_map() -> Result<HeaderMap, Error>) so invalid
HeaderName::from_str / HeaderValue::from_str errors are returned instead of
panicking; update callers to handle the Result, or alternatively if you prefer
best-effort behavior, replace the expect() calls inside from() with match/if let
to skip invalid entries and optionally log a warning (reference ApiKeys,
api_keys.keys, and the from method when making the change).
🧹 Nitpick comments (3)
src/common/inference/service.rs (1)

218-223: Prefer explicit type conversion over .into().

As per coding guidelines, prefer explicit SomeType::from(x) over implicit x.into() in Rust.

Suggested change
         let mut request = request.json(&request_body);
         if let Some(api_keys) = ext_api_keys {
-            request = request.headers(api_keys.into());
+            request = request.headers(reqwest::header::HeaderMap::from(api_keys));
         }
src/common/inference/ext_api_keys.rs (2)

35-37: Consider avoiding the clone of MetadataMap.

metadata.clone().into_headers() allocates a full copy of the metadata map. If performance is a concern for high-throughput gRPC calls, consider iterating directly over the metadata entries instead.

Alternative implementation without clone
     pub fn from_grpc_metadata(metadata: &tonic::metadata::MetadataMap) -> Self {
-        Self::from_headers(&metadata.clone().into_headers().into())
+        let mut api_keys = Self::default();
+        for key_and_value in metadata.iter() {
+            if let tonic::metadata::KeyAndValueRef::Ascii(key, value) = key_and_value {
+                let k = key.as_str();
+                if k.ends_with(EMBEDDING_API_KEY_HEADER_SUFFIX) {
+                    if let Ok(v) = value.to_str() {
+                        api_keys.keys.insert(k.to_string(), v.to_string());
+                    }
+                }
+            }
+        }
+        api_keys
     }

52-54: Consider removing this thin wrapper function.

extract_api_key simply delegates to ApiKeys::from_grpc_metadata. Unless there's a specific reason for this indirection (e.g., future extension), calling the method directly would be more idiomatic.

@dancixx dancixx marked this pull request as draft January 20, 2026 11:45
@dancixx dancixx marked this pull request as ready for review January 20, 2026 23:36
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/common/inference/api_keys.rs`:
- Around line 114-126: The From<InferenceApiKeys> impl for
reqwest::header::HeaderMap currently uses expect() on HeaderName::from_str and
HeaderValue::from_str which will panic on invalid user input; modify the
conversion to handle invalid headers gracefully by either changing the API to
return a Result<reqwest::header::HeaderMap, E> from a new TryFrom/TryInto
implementation for InferenceApiKeys (propagate parse errors) or by
filtering/logging invalid entries inside fn from (skip entries where
HeaderName::from_str or HeaderValue::from_str returns Err and record a warning),
updating usage sites accordingly; reference the impl block for
From<InferenceApiKeys>, the from(fn) method, and the api_keys.keys iteration to
locate the code to change.
🧹 Nitpick comments (1)
src/actix/api/update_api.rs (1)

326-327: Minor: Inconsistent attribute ordering.

The attribute ordering here (#[allow] before #[post]) differs from upsert_points and update_vectors, which both place the route attribute (#[put]/#[post]) before #[allow(clippy::too_many_arguments)]. Consider reordering for consistency:

🔧 Suggested reorder for consistency
-#[allow(clippy::too_many_arguments)]
 #[post("/collections/{name}/points/batch")]
+#[allow(clippy::too_many_arguments)]
 async fn update_batch(

@dancixx dancixx merged commit be0ba78 into dev Feb 2, 2026
12 checks passed
@dancixx dancixx deleted the feat/ext-api-keys branch February 2, 2026 13:25
@coderabbitai coderabbitai bot mentioned this pull request Feb 7, 2026
generall added a commit that referenced this pull request Feb 9, 2026
* feat: forward api keys header

* fix: linter

* feat: add api_keys to grpc

* fix: imports

* fix: extract api keys from grpc headers

* refactor: make apikeys extracor more compact

* fix: linter issue

* feat: update header key logic

* fix: clippy issues

* make external keys a part of InferenceParams::new argument, so we wont forget using it

* avoid cloning metadata on header extraction

* combine inference token with ext api key for simpler usage

* clippy

---------

Co-authored-by: generall <andrey@vasnetsov.com>
@timvisee timvisee mentioned this pull request Feb 17, 2026
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants