Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions wavefront/client/src/components/DashboardLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ interface IUser {
const DashboardLayout = ({ user, apps = [] }: { user: IUser; apps: App[] }) => {
const currentPath = useLocation();
const navigate = useNavigate();
const timeoutRef = useRef<number | null>(null);
const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
/*
The Broadcast Channel API enables communication between different browser windows, tabs, iframes, and web workers.
creating a channel with name 'timeout'
Expand All @@ -27,7 +27,7 @@ const DashboardLayout = ({ user, apps = [] }: { user: IUser; apps: App[] }) => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
// @ts-ignore

timeoutRef.current = setTimeout(
() => {
navigate('/logout');
Expand Down
13 changes: 13 additions & 0 deletions wavefront/client/src/config/llm-providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface ParameterConfig {

export interface ProviderConfig {
name: string;
baseUrl?: string;
badge: {
bg: string;
text: string;
Expand All @@ -36,6 +37,7 @@ export type InferenceEngineType = 'gemini' | 'openai' | 'ollama' | 'vllm' | 'ant
export const LLM_PROVIDERS_CONFIG: Record<InferenceEngineType, ProviderConfig> = {
openai: {
name: 'OpenAI GPT',
baseUrl: 'https://api.openai.com/v1',
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Anthropic Claude API base URL official documentation

💡 Result:

The official Anthropic Claude API base URL is: https://api.anthropic.com/v1/. [1][2]

Sources:

  • Anthropic API docs / release notes. [1]
  • Anthropic Help Center (API docs link and examples). [2]

Add /v1 to the Anthropic base URL.

The Anthropic base URL should be https://api.anthropic.com/v1/ according to official Anthropic API documentation. Update line 113 to include the /v1 path segment.

Also applies to: 113-113

🤖 Prompt for AI Agents
In wavefront/client/src/config/llm-providers.ts around line 113, the Anthropic
provider baseUrl is missing the required /v1 path segment; update the Anthropic
baseUrl value to include the API version (e.g. "https://api.anthropic.com/v1/"
or "https://api.anthropic.com/v1") so requests target the correct endpoint and
match the official documentation.

badge: {
bg: 'bg-green-100',
text: 'text-green-800',
Expand Down Expand Up @@ -108,6 +110,7 @@ export const LLM_PROVIDERS_CONFIG: Record<InferenceEngineType, ProviderConfig> =
},
anthropic: {
name: 'Anthropic Claude',
baseUrl: 'https://api.anthropic.com',
badge: {
bg: 'bg-orange-100',
text: 'text-orange-800',
Expand Down Expand Up @@ -153,6 +156,7 @@ export const LLM_PROVIDERS_CONFIG: Record<InferenceEngineType, ProviderConfig> =
},
gemini: {
name: 'Google Gemini',
baseUrl: 'https://generativelanguage.googleapis.com',
badge: {
bg: 'bg-blue-100',
text: 'text-blue-800',
Expand Down Expand Up @@ -261,6 +265,7 @@ export const LLM_PROVIDERS_CONFIG: Record<InferenceEngineType, ProviderConfig> =
},
groq: {
name: 'Groq',
baseUrl: 'https://api.groq.com/openai/v1',
badge: {
bg: 'bg-purple-100',
text: 'text-purple-800',
Expand Down Expand Up @@ -509,3 +514,11 @@ export function cleanParameters(params: Record<string, any>): Record<string, any
});
return cleaned;
}

/**
* Get default base URL for a provider
*/
export function getDefaultBaseUrl(provider: InferenceEngineType): string {
const config = getProviderConfig(provider);
return config?.baseUrl || '';
}
2 changes: 1 addition & 1 deletion wavefront/client/src/pages/apps/[appId]/agents/[id].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ const AgentDetail: React.FC = () => {
// Prepare inputs based on what's provided
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let inputs: string | any[];
let finalTextInput = inferenceInput.trim();
const finalTextInput = inferenceInput.trim();
// Handle different input combinations
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const conversationInputs: any[] = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,7 @@ const DatasourceDetail: React.FC = () => {
if (!datasourceId) return;
if (yamlContent) {
const yamlResponse = validateDynamicQueryYaml(yamlContent);
if (yamlResponse.valid) {
} else {
if (!yamlResponse.valid) {
notifyError(yamlResponse.error);
return;
}
Expand Down Expand Up @@ -190,6 +189,7 @@ const DatasourceDetail: React.FC = () => {
notifySuccess('Datasource deleted successfully');
navigate(`/apps/${appId}/datasources`);
} catch (error) {
console.error('Error deleting datasource');
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Include the error object in the log for debugging.

Logging a static string without the actual error loses valuable debugging information when troubleshooting production issues.

-      console.error('Error deleting datasource');
+      console.error('Error deleting datasource:', error);

Additionally, consider notifying the user about the failure since the error is currently swallowed silently:

     } catch (error) {
       console.error('Error deleting datasource:', error);
+      notifyError('Failed to delete datasource');
     } finally {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
console.error('Error deleting datasource');
console.error('Error deleting datasource:', error);
🤖 Prompt for AI Agents
In wavefront/client/src/pages/apps/[appId]/datasources/[datasourceId].tsx around
line 192, the catch block logs only a static string "Error deleting datasource"
which omits the caught error details and swallows the failure; update the catch
to log both the message and the actual error object (e.g., process the caught
error variable in the log call) and add user-facing feedback (toast, alert, or
set an error state) so the user is notified of the deletion failure.

} finally {
setDeleting(false);
setShowDeleteConfirm(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,13 @@ import {
import { Input } from '@app/components/ui/input';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@app/components/ui/select';
import { Slider } from '@app/components/ui/slider';
import { cleanParameters, getProviderConfig, initializeParameters, ParameterConfig } from '@app/config/llm-providers';
import {
cleanParameters,
getDefaultBaseUrl,
getProviderConfig,
initializeParameters,
ParameterConfig,
} from '@app/config/llm-providers';
import { useDashboardStore, useNotifyStore } from '@app/store';
import { InferenceEngineType } from '@app/types/llm-inference-config';
import { zodResolver } from '@hookform/resolvers/zod';
Expand Down Expand Up @@ -55,10 +61,10 @@ const BASE_URL_PLACEHOLDERS: Record<InferenceEngineType, string> = {
ollama: 'http://localhost:11434',
vllm: 'http://localhost:8000',
azure_openai: 'https://your-resource.openai.azure.com',
openai: 'Base URL',
anthropic: 'Base URL',
gemini: 'Base URL',
groq: 'Base URL',
openai: 'https://api.openai.com/v1',
anthropic: 'https://api.anthropic.com',
gemini: 'https://generativelanguage.googleapis.com',
groq: 'https://api.groq.com/openai/v1',
};

const createLLMInferenceSchema = z.object({
Expand Down Expand Up @@ -98,7 +104,7 @@ const CreateLLMInferenceDialog: React.FC<CreateLLMInferenceDialogProps> = ({
llmModel: '',
type: 'openai',
apiKey: '',
baseUrl: '',
baseUrl: getDefaultBaseUrl('openai'),
},
});

Expand All @@ -110,21 +116,26 @@ const CreateLLMInferenceDialog: React.FC<CreateLLMInferenceDialogProps> = ({
setType(newType);
const defaultParams = initializeParameters(newType);
setParameters(defaultParams);

// Set default base URL for the provider
const defaultBaseUrl = getDefaultBaseUrl(newType);
form.setValue('baseUrl', defaultBaseUrl);
}
}, [watchedType, isOpen]);
}, [watchedType, isOpen, form]);

// Reset form when dialog closes
useEffect(() => {
if (!isOpen) {
const defaultType = 'openai';
form.reset({
displayName: '',
llmModel: '',
type: 'openai',
type: defaultType,
apiKey: '',
baseUrl: '',
baseUrl: getDefaultBaseUrl(defaultType),
});
setType('openai');
setParameters(initializeParameters('openai'));
setType(defaultType);
setParameters(initializeParameters(defaultType));
}
}, [isOpen, form]);

Expand All @@ -133,7 +144,7 @@ const CreateLLMInferenceDialog: React.FC<CreateLLMInferenceDialogProps> = ({
};

const supportsBaseUrl = (engineType: InferenceEngineType) => {
return ['ollama', 'vllm', 'azure_openai'].includes(engineType);
return ['ollama', 'vllm', 'azure_openai', 'openai', 'anthropic', 'gemini', 'groq'].includes(engineType);
};

const setParameter = (key: string, value: any) => {
Expand Down Expand Up @@ -330,6 +341,10 @@ const CreateLLMInferenceDialog: React.FC<CreateLLMInferenceDialogProps> = ({
const newType = value as InferenceEngineType;
setType(newType);
setParameters(initializeParameters(newType));

// Set default base URL for the provider
const defaultBaseUrl = getDefaultBaseUrl(newType);
form.setValue('baseUrl', defaultBaseUrl);
}}
value={field.value}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@ import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '
import { Input } from '@app/components/ui/input';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@app/components/ui/select';
import { Slider } from '@app/components/ui/slider';
import { cleanParameters, getProviderConfig, mergeParameters, ParameterConfig } from '@app/config/llm-providers';
import {
cleanParameters,
getDefaultBaseUrl,
getProviderConfig,
mergeParameters,
ParameterConfig,
} from '@app/config/llm-providers';
import { useGetLLMConfig } from '@app/hooks';
import { getLLMConfigKey, getLLMConfigsKey } from '@app/hooks/data/query-keys';
import { useNotifyStore } from '@app/store';
Expand Down Expand Up @@ -92,13 +98,19 @@ const LLMInferenceConfigDetail: React.FC = () => {
}
}, [config, form]);

// Update parameters when provider type changes in edit mode
// Update parameters and base URL when provider type changes in edit mode
const watchedType = form.watch('type');
useEffect(() => {
if (editing && config) {
const mergedParams = mergeParameters(watchedType, watchedType === config.type ? config.parameters : null);
setParameters(mergedParams);
form.setValue('parameters', mergedParams);

// Update base URL to the new provider's default when type changes
if (watchedType !== config.type) {
const defaultBaseUrl = getDefaultBaseUrl(watchedType);
form.setValue('base_url', defaultBaseUrl);
}
}
}, [watchedType, editing, config, form]);

Expand Down Expand Up @@ -179,7 +191,7 @@ const LLMInferenceConfigDetail: React.FC = () => {
};

const supportsBaseUrl = (engineType: InferenceEngineType) => {
return ['ollama', 'vllm', 'azure_openai'].includes(engineType);
return ['ollama', 'vllm', 'azure_openai', 'openai', 'anthropic', 'gemini', 'groq'].includes(engineType);
};

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
import requests
from rag_ingestion.env import EMBEDDING_SERVICE_URL
from flo_utils.utils.log import logger
from rag_ingestion.env import OPENAI_API_KEY, EMBEDDING_MODEL


class EmbeddingFunc:
def __init__(self):
self.max_batch_size = 32
self.bgm_url = f'{EMBEDDING_SERVICE_URL}/v1/embeddings'
self.bgm_url = f'{EMBEDDING_SERVICE_URL}'
logger.info(f'The embedding url is {EMBEDDING_SERVICE_URL}')

def generate_document_embeddings(self, chunks):
Expand All @@ -33,12 +34,20 @@ def generate_chunk_embeddings(self, chunks):
return embeddings

def bgm_embedding(self, texts):
headers = {
'Authorization': f'Bearer {OPENAI_API_KEY}',
}

response = requests.post(
self.bgm_url,
headers=headers if EMBEDDING_MODEL == 'text-embedding-3-small' else None,
json={
'model': 'BAAI/bge-m3',
'model': EMBEDDING_MODEL,
'input': texts,
'encoding_format': 'float',
},
timeout=60,
)
Comment on lines 41 to 50
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add timeout to prevent indefinite hangs.

The requests.post call lacks a timeout, which can cause the background job to hang indefinitely if the embedding service is unresponsive.

         response = requests.post(
             self.bgm_url,
             headers={'Authorization': 'Bearer ' + openai_api_key},
             json={
                 # 'model': 'BAAI/bge-m3',
                 'model': 'text-embedding-3-small',
                 'input': texts,
                 'encoding_format': 'float',
             },
+            timeout=60,
         )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
response = requests.post(
self.bgm_url,
headers={'Authorization': 'Bearer ' + openai_api_key},
json={
'model': 'BAAI/bge-m3',
# 'model': 'BAAI/bge-m3',
'model': 'text-embedding-3-small',
'input': texts,
'encoding_format': 'float',
},
)
response = requests.post(
self.bgm_url,
headers={'Authorization': 'Bearer ' + openai_api_key},
json={
# 'model': 'BAAI/bge-m3',
'model': 'text-embedding-3-small',
'input': texts,
'encoding_format': 'float',
},
timeout=60,
)
🧰 Tools
🪛 Ruff (0.14.8)

38-38: Probable use of requests call without timeout

(S113)

🤖 Prompt for AI Agents
In
wavefront/server/background_jobs/rag_ingestion/rag_ingestion/embeddings/embed.py
around lines 38 to 47, the requests.post call has no timeout which can cause the
job to hang indefinitely; modify the call to pass a reasonable timeout parameter
(e.g., timeout=10 or configurable via an environment variable) and handle
timeout-related exceptions (requests.Timeout and requests.RequestException) so
the job can retry or fail gracefully with a logged error.

return response.json()['data'][0]['embedding']
response.raise_for_status()
res = response.json()
return res['data'][0]['embedding']
Comment on lines +52 to +53
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Returns only the first embedding; batch processing is broken.

When input contains multiple texts, the OpenAI API returns embeddings for all of them in res['data']. This code discards all but the first, breaking batch processing.

Additionally, there's no error handling for HTTP failures or unexpected responses.

-        res = response.json()
-        return res['data'][0]['embedding']
+        response.raise_for_status()
+        res = response.json()
+        return [item['embedding'] for item in res['data']]

This returns all embeddings in order, which pairs with the fix needed at line 20.

Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@

CLOUD_PROVIDER = os.getenv('CLOUD_PROVIDER', 'gcp')
RETRY_COUNT = os.getenv('RETRY_COUNT', 3)
OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find all usages of OPENAI_API_KEY in the codebase
echo "=== Searching for OPENAI_API_KEY usages ==="
rg -n "OPENAI_API_KEY" --type=py -C3

echo ""
echo "=== Searching for OpenAI client initialization ==="
rg -n "openai\.(Client|OpenAI|AzureOpenAI)" --type=py -C3

echo ""
echo "=== Searching for from_env or similar patterns ==="
rg -n "from_env|api_key.*=|Client\(" --type=py | head -50

Repository: rootflo/wavefront

Length of output: 22707


🏁 Script executed:

cat -n wavefront/server/background_jobs/rag_ingestion/rag_ingestion/embeddings/embed.py

Repository: rootflo/wavefront

Length of output: 2262


Add validation to ensure OPENAI_API_KEY is set when required.

When EMBEDDING_MODEL is 'text-embedding-3-small' (the default), the code interpolates OPENAI_API_KEY directly into the Authorization header without checking if it's None. If the environment variable is not set, this will create a malformed header with 'Bearer None' that will fail at runtime. Either validate that OPENAI_API_KEY is not None before using it in the header, or raise a clear error on initialization if it's required but missing.

🤖 Prompt for AI Agents
In wavefront/server/background_jobs/rag_ingestion/rag_ingestion/env.py around
line 8, OPENAI_API_KEY is read but not validated and may be None when
EMBEDDING_MODEL == 'text-embedding-3-small', producing a malformed "Bearer None"
header; update initialization to explicitly check if EMBEDDING_MODEL requires an
API key and if so validate that OPENAI_API_KEY is set (raise a clear
RuntimeError or ValueError with a descriptive message at startup), or
alternatively only include the Authorization header when OPENAI_API_KEY is
non-empty and fail fast with a helpful error if the model requires it.

EMBEDDING_SERVICE_URL = os.getenv('EMBEDDING_SERVICE_URL')
FLOWARE_SERVICE_URL = os.getenv('FLOWARE_SERVICE_URL')
APP_ENV = os.getenv('APP_ENV', 'dev')
PASSTHROUGH_SECRET = os.getenv('PASSTHROUGH_SECRET')
EMBEDDING_MODEL = os.getenv('EMBEDDING_MODEL', 'text-embedding-3-small')
Original file line number Diff line number Diff line change
Expand Up @@ -82,16 +82,17 @@ async def create_knowledge_base(
)


@knowledge_base_router.get(
'/v1/knowledge-bases/{kb_id}', response_model=KnowledgeBaseResponse
)
@knowledge_base_router.get('/v1/knowledge-bases/{kb_id}')
@inject
async def get_knowledge_bases_id(
kb_id: uuid.UUID,
response_formatter: ResponseFormatter = Depends(
Provide[CommonContainer.response_formatter]
),
knowledge_base_repository: SQLAlchemyRepository[KnowledgeBase] = Depends(
Provide[KnowledgeBaseContainer.knowledge_base_repository]
),
) -> KnowledgeBaseResponse:
) -> JSONResponse:
"""Get knowledge base by ID."""
fetch_knowledge_base_id = await knowledge_base_repository.find_one(id=kb_id)
if not fetch_knowledge_base_id:
Expand All @@ -100,13 +101,11 @@ async def get_knowledge_bases_id(
detail="Knowledge Base with the mentioned id doesn't exist",
)

return KnowledgeBaseResponse(
id=fetch_knowledge_base_id.id,
name=fetch_knowledge_base_id.name,
description=fetch_knowledge_base_id.description,
type=fetch_knowledge_base_id.type,
created_at=fetch_knowledge_base_id.created_at,
updated_at=fetch_knowledge_base_id.updated_at,
return JSONResponse(
status_code=status.HTTP_200_OK,
content=response_formatter.buildSuccessResponse(
data=fetch_knowledge_base_id.to_dict()
),
)


Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
import requests
from typing import List
from dataclasses import dataclass
Expand All @@ -13,7 +14,7 @@ class KnowledgeBaseEmbeddingObject:
class EmbeddingFunc:
def __init__(self, embedding_url):
self.max_batch_size = 32
self.bgm_url = f'{embedding_url}/v1/embeddings'
self.bgm_url = f'{embedding_url}'

def generate_document_embeddings(self, chunks):
contents = [v['content'] for v in chunks.values()]
Expand Down Expand Up @@ -42,12 +43,16 @@ def generate_chunk_embeddings(self, chunks):
return embeddings

def bgm_embedding(self, texts):
openai_api_key = os.getenv('OPENAI_API_KEY') or ''
response = requests.post(
self.bgm_url,
headers={'Authorization': 'Bearer ' + openai_api_key},
json={
'model': 'BAAI/bge-m3',
# 'model': 'BAAI/bge-m3',
'model': 'text-embedding-3-small',
Comment on lines +51 to +52
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Search for references to the old BAAI/bge-m3 model or its typical vector size
rg -n "BAAI/bge-m3|bge-m3" --type py

# Search for vector_size configurations that might need updating
rg -n "vector_size" --type py -C 2

Repository: rootflo/wavefront

Length of output: 25282


🏁 Script executed:

# Read the embed.py file at the target lines to see context
head -70 wavefront/server/modules/knowledge_base_module/knowledge_base_module/embeddings/embed.py | tail -30

# Check if both embed.py files have similar changes
head -60 wavefront/server/background_jobs/rag_ingestion/rag_ingestion/embeddings/embed.py | tail -20

Repository: rootflo/wavefront

Length of output: 1727


🏁 Script executed:

# Search for bgm_url definition
rg -n "bgm_url" --type py -B 3 -A 3

# Search for any migration-related code or documentation
rg -n "migration|migrate|regenerate" --type py -i | head -30

# Check for configuration or setup documentation
fd -e "README|SETUP|CONFIG|\.md" -i | head -20

Repository: rootflo/wavefront

Length of output: 7156


🏁 Script executed:

# Search for embedding deletion or regeneration logic
rg -n "delete.*embedding|regenerate|re-embed" --type py -i

# Check if embeddings are generated on-demand or stored
rg -n "generate.*embedding|store.*embedding" --type py -A 2 | head -40

# Look for any data migration scripts
fd -e "py" -path "*/alembic/versions/*" -exec basename {} \; | grep -i embed

Repository: rootflo/wavefront

Length of output: 5787


🌐 Web query:

BAAI bge-m3 embedding dimensions output size

💡 Result:

BAAI's BGE‑M3 produces 1024‑dimensional embeddings (output vector length = 1024). [1][2][3]

Sources:

  • BGE docs (BAAI/bge-m3) — specifies model and embedding specs. [1]
  • Hugging Face discussion for BAAI/bge-m3 — confirms embedding dimension = 1024. [2]
  • Model summaries (e.g., PromptLayer/IONOS) — lists 1024 output dimensionality. [3]

Verify embedding migration strategy before deploying the model change.

The switch from BAAI/bge-m3 (1024 dimensions) to text-embedding-3-small (1536 dimensions) introduces a dimension incompatibility. Vector size validation in rag_retreival_controller.py (lines 449-452) will reject any existing embeddings generated with the old model. Ensure:

  1. Existing embeddings are deleted or regenerated before enabling the new model.
  2. The embedding service endpoint (bgm_url / EMBEDDING_SERVICE_URL) is configured to serve text-embedding-3-small.
  3. Document ingestion workflows trigger re-embedding of all knowledge bases post-deployment.
🤖 Prompt for AI Agents
In
wavefront/server/modules/knowledge_base_module/knowledge_base_module/embeddings/embed.py
around lines 51-52, the model change from 'BAAI/bge-m3' (1024d) to
'text-embedding-3-small' (1536d) will break vector-size validation; before
deploying, implement a migration plan: add or run a script to detect and delete
or regenerate all existing embeddings (or mark KBs as needing re-embed), ensure
EMBEDDING_SERVICE_URL/bgm_url is configured to serve text-embedding-3-small,
update deployment docs and ingestion workflows to trigger re-embedding of every
knowledge base after rollout, and (optionally) add a runtime safety check that
prevents switching models unless a forced migration flag is supplied.

'input': texts,
'encoding_format': 'float',
},
)
return response.json()['data'][0]['embedding']
res = response.json()
return res['data'][0]['embedding']
Comment on lines 45 to +58
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Add timeout, error handling, and validate API key.

The current implementation has several issues:

  1. Missing timeout (line 47): The requests.post call lacks a timeout parameter, which can cause indefinite hangs if the API is unresponsive.
  2. No error handling: API failures (network errors, 4xx/5xx responses) will raise unhandled exceptions.
  3. Empty string fallback (line 46): Using an empty string when OPENAI_API_KEY is missing will cause authentication failures without clear error messages.

Apply this diff to address these issues:

 def bgm_embedding(self, texts):
     openai_api_key = os.getenv('OPENAI_API_KEY') or ''
+    if not openai_api_key:
+        raise ValueError('OPENAI_API_KEY environment variable is required')
+    
     response = requests.post(
         self.bgm_url,
         headers={'Authorization': 'Bearer ' + openai_api_key},
         json={
             # 'model': 'BAAI/bge-m3',
             'model': 'text-embedding-3-small',
             'input': texts,
             'encoding_format': 'float',
         },
+        timeout=30,
     )
+    response.raise_for_status()
     res = response.json()
     return res['data'][0]['embedding']
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def bgm_embedding(self, texts):
openai_api_key = os.getenv('OPENAI_API_KEY') or ''
response = requests.post(
self.bgm_url,
headers={'Authorization': 'Bearer ' + openai_api_key},
json={
'model': 'BAAI/bge-m3',
# 'model': 'BAAI/bge-m3',
'model': 'text-embedding-3-small',
'input': texts,
'encoding_format': 'float',
},
)
return response.json()['data'][0]['embedding']
res = response.json()
return res['data'][0]['embedding']
def bgm_embedding(self, texts):
openai_api_key = os.getenv('OPENAI_API_KEY') or ''
if not openai_api_key:
raise ValueError('OPENAI_API_KEY environment variable is required')
response = requests.post(
self.bgm_url,
headers={'Authorization': 'Bearer ' + openai_api_key},
json={
# 'model': 'BAAI/bge-m3',
'model': 'text-embedding-3-small',
'input': texts,
'encoding_format': 'float',
},
timeout=30,
)
response.raise_for_status()
res = response.json()
return res['data'][0]['embedding']
🧰 Tools
🪛 Ruff (0.14.8)

47-47: Probable use of requests call without timeout

(S113)

🤖 Prompt for AI Agents
In
wavefront/server/modules/knowledge_base_module/knowledge_base_module/embeddings/embed.py
around lines 45 to 58, the bgm_embedding method lacks API key validation,
timeout, and robust error handling; update it to (1) validate that
OPENAI_API_KEY exists and raise/log a clear error if missing instead of using an
empty string, (2) call requests.post with a sensible timeout (e.g., 10s) to
avoid hangs, (3) wrap the request in a try/except to catch requests.exceptions
(e.g., ConnectionError, Timeout, HTTPError) and raise or return a clear error,
and (4) check response.status_code and handle non-2xx responses by parsing any
error message from the response and raising/logging a descriptive exception
before attempting to access res['data'][0]['embedding'] so you never assume a
successful JSON shape.