Skip to content
Open
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
22 changes: 16 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,23 @@ Copy your Personal Key to replace `your-zilliz-cloud-api-key` in the configurati
</details>

<details>
<summary>Get OpenAI API Key for embedding model</summary>
<summary>Get an API Key for embedding model</summary>

You need an OpenAI API key for the embedding model. You can get one by signing up at [OpenAI](https://platform.openai.com/api-keys).
You need an API key for the embedding model. Claude Context supports multiple providers:

Your API key will look like this: it always starts with `sk-`.
Copy your key and use it in the configuration examples below as `your-openai-api-key`.
**Option 1: OpenAI**
- Sign up at [OpenAI](https://platform.openai.com/api-keys)
- Your API key will start with `sk-`
- Use as `your-openai-api-key` in configuration

**Option 2: Azure OpenAI**
- Use your Azure OpenAI resource endpoint and API key
- Requires deployment name instead of model name
- See [Azure OpenAI Documentation](https://learn.microsoft.com/en-us/azure/ai-services/openai/)

**Option 3: Other Providers**
- VoyageAI, Gemini, or Ollama (local)
- See [Provider Configuration Guide](packages/mcp/README.md#embedding-provider-configuration) for details

</details>

Expand Down Expand Up @@ -519,8 +530,7 @@ Claude Context is a monorepo containing three main packages:
- **`@zilliz/claude-context-mcp`**: Model Context Protocol server for AI agent integration

### Supported Technologies

- **Embedding Providers**: [OpenAI](https://openai.com), [VoyageAI](https://voyageai.com), [Ollama](https://ollama.ai), [Gemini](https://gemini.google.com)
- **Embedding Providers**: [OpenAI](https://openai.com), [Azure OpenAI](https://azure.microsoft.com/en-us/products/ai-services/openai-service), [VoyageAI](https://voyageai.com), [Ollama](https://ollama.ai), [Gemini](https://gemini.google.com)
- **Vector Databases**: [Milvus](https://milvus.io) or [Zilliz Cloud](https://zilliz.com/cloud)(fully managed vector database as a service)
- **Code Splitters**: AST-based splitter (with automatic fallback), LangChain character-based splitter
- **Languages**: TypeScript, JavaScript, Python, Java, C++, C#, Go, Rust, PHP, Ruby, Swift, Kotlin, Scala, Markdown
Expand Down
22 changes: 21 additions & 1 deletion docs/getting-started/environment-variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,14 @@ Claude Context supports a global configuration file at `~/.context/.env` to simp
### Embedding Provider
| Variable | Description | Default |
|----------|-------------|---------|
| `EMBEDDING_PROVIDER` | Provider: `OpenAI`, `VoyageAI`, `Gemini`, `Ollama` | `OpenAI` |
| `EMBEDDING_PROVIDER` | Provider: `OpenAI`, `AzureOpenAI`, `VoyageAI`, `Gemini`, `Ollama` | `OpenAI` |
| `EMBEDDING_MODEL` | Embedding model name (works for all providers) | Provider-specific default |
| `OPENAI_API_KEY` | OpenAI API key | Required for OpenAI |
| `OPENAI_BASE_URL` | OpenAI API base URL (optional, for custom endpoints) | `https://api.openai.com/v1` |
| `AZURE_OPENAI_API_KEY` | Azure OpenAI API key | Required for Azure OpenAI |
| `AZURE_OPENAI_ENDPOINT` | Azure OpenAI endpoint URL | Required for Azure OpenAI |
| `AZURE_OPENAI_DEPLOYMENT_NAME` | Azure deployment name | Required for Azure OpenAI |
| `AZURE_OPENAI_API_VERSION` | Azure API version | `2024-02-01` |
| `VOYAGEAI_API_KEY` | VoyageAI API key | Required for VoyageAI |
| `GEMINI_API_KEY` | Gemini API key | Required for Gemini |
| `GEMINI_BASE_URL` | Gemini API base URL (optional, for custom endpoints) | `https://generativelanguage.googleapis.com/v1beta` |
Expand All @@ -33,6 +37,8 @@ Claude Context supports a global configuration file at `~/.context/.env` to simp
> **Supported Model Names:**
>
> - OpenAI Models: See `getSupportedModels` in [`openai-embedding.ts`](https://github.com/zilliztech/claude-context/blob/master/packages/core/src/embedding/openai-embedding.ts) for the full list of supported models.
>
> - Azure OpenAI: Uses deployment names instead of model names. Supports the same models as OpenAI (text-embedding-3-small, text-embedding-3-large, text-embedding-ada-002).
>
> - VoyageAI Models: See `getSupportedModels` in [`voyageai-embedding.ts`](https://github.com/zilliztech/claude-context/blob/master/packages/core/src/embedding/voyageai-embedding.ts) for the full list of supported models.
>
Expand Down Expand Up @@ -67,6 +73,8 @@ Claude Context supports a global configuration file at `~/.context/.env` to simp
## 🚀 Quick Setup

### 1. Create Global Config

**Option A: OpenAI**
```bash
mkdir -p ~/.context
cat > ~/.context/.env << 'EOF'
Expand All @@ -77,6 +85,18 @@ MILVUS_TOKEN=your-zilliz-cloud-api-key
EOF
```

**Option B: Azure OpenAI**
```bash
mkdir -p ~/.context
cat > ~/.context/.env << 'EOF'
EMBEDDING_PROVIDER=AzureOpenAI
AZURE_OPENAI_API_KEY=your-azure-api-key
AZURE_OPENAI_ENDPOINT=https://your-resource.openai.azure.com
AZURE_OPENAI_DEPLOYMENT_NAME=text-embedding-3-small-deployment
MILVUS_TOKEN=your-zilliz-cloud-api-key
EOF
```

See the [Example File](../../.env.example) for more details.

### 2. Simplified MCP Configuration
Expand Down
25 changes: 25 additions & 0 deletions packages/core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ results.forEach(result => {
## Embedding Providers

- **OpenAI Embeddings** (`text-embedding-3-small`, `text-embedding-3-large`, `text-embedding-ada-002`)
- **Azure OpenAI Embeddings** - Same models as OpenAI, deployed on Azure infrastructure
- **VoyageAI Embeddings** - High-quality embeddings optimized for code (`voyage-code-3`, `voyage-3.5`, etc.)
- **Gemini Embeddings** - Google's embedding models (`gemini-embedding-001`)
- **Ollama Embeddings** - Local embedding models via Ollama
Expand Down Expand Up @@ -190,6 +191,30 @@ interface SemanticSearchResult {

## Examples

### Using Azure OpenAI Embeddings

```typescript
import { Context, MilvusVectorDatabase, AzureOpenAIEmbedding } from '@zilliz/claude-context-core';

// Initialize with Azure OpenAI embedding provider
const embedding = new AzureOpenAIEmbedding({
deploymentName: 'text-embedding-3-small-deployment',
apiKey: process.env.AZURE_OPENAI_API_KEY || 'your-azure-api-key',
azureEndpoint: process.env.AZURE_OPENAI_ENDPOINT || 'https://your-resource.openai.azure.com',
apiVersion: '2024-02-01' // Optional
});

const vectorDatabase = new MilvusVectorDatabase({
address: process.env.MILVUS_ADDRESS || 'localhost:19530',
token: process.env.MILVUS_TOKEN || ''
});

const context = new Context({
embedding,
vectorDatabase
});
```

### Using VoyageAI Embeddings

```typescript
Expand Down
256 changes: 256 additions & 0 deletions packages/core/src/embedding/azure-openai-embedding.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
import { AzureOpenAI } from 'openai';
import { Embedding, EmbeddingVector } from './base-embedding';

export interface AzureOpenAIEmbeddingConfig {
deploymentName: string; // Azure deployment name (not model name)
apiKey: string; // Azure OpenAI API key
azureEndpoint: string; // Required: Azure endpoint URL
apiVersion?: string; // Optional: defaults to stable version
}

export class AzureOpenAIEmbedding extends Embedding {
private client: AzureOpenAI;
private config: AzureOpenAIEmbeddingConfig;
private dimension: number = 1536; // Default dimension for text-embedding-3-small
protected maxTokens: number = 8192; // Maximum tokens for OpenAI embedding models

constructor(config: AzureOpenAIEmbeddingConfig) {
super();
this.config = config;

// Validate endpoint format
if (!config.azureEndpoint.startsWith('https://')) {
throw new Error('Azure OpenAI endpoint must start with https://');
}

// Initialize Azure OpenAI client with API key authentication
this.client = new AzureOpenAI({
apiKey: config.apiKey,
apiVersion: config.apiVersion || '2024-02-01', // Use stable version
endpoint: config.azureEndpoint,
});
}

async detectDimension(testText: string = "test"): Promise<number> {
const knownModels = AzureOpenAIEmbedding.getSupportedModels();

// Try to infer from deployment name if it matches known patterns
// Azure deployment names often include the model name with dashes
for (const [modelName, info] of Object.entries(knownModels)) {
// Check if deployment name contains model pattern (with dashes instead of dots)
const modelPattern = modelName.replace(/\./g, '-');
if (this.config.deploymentName.toLowerCase().includes(modelPattern)) {
return info.dimension;
}
}

// Dynamic detection via API call for custom deployments
try {
const processedText = this.preprocessText(testText);
const response = await this.client.embeddings.create({
model: this.config.deploymentName, // Use deployment name
input: processedText,
encoding_format: 'float',
});
return response.data[0].embedding.length;
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';

// Re-throw authentication errors
if (errorMessage.includes('API key') || errorMessage.includes('unauthorized') || errorMessage.includes('authentication')) {
throw new Error(`Azure OpenAI authentication failed: ${errorMessage}`);
}

// Handle deployment not found errors
if (errorMessage.includes('deployment') || errorMessage.includes('not found')) {
throw new Error(`Azure OpenAI deployment '${this.config.deploymentName}' not found: ${errorMessage}`);
}

throw new Error(`Failed to detect dimension for Azure deployment ${this.config.deploymentName}: ${errorMessage}`);
}
}

async embed(text: string): Promise<EmbeddingVector> {
const processedText = this.preprocessText(text);

// Check if we need to detect dimension
const knownModels = AzureOpenAIEmbedding.getSupportedModels();
let needsDimensionDetection = true;

for (const [modelName, info] of Object.entries(knownModels)) {
const modelPattern = modelName.replace(/\./g, '-');
if (this.config.deploymentName.toLowerCase().includes(modelPattern)) {
this.dimension = info.dimension;
needsDimensionDetection = false;
break;
}
}

if (needsDimensionDetection && this.dimension === 1536) {
// Only detect if we haven't already and are using default
this.dimension = await this.detectDimension();
}

try {
const response = await this.client.embeddings.create({
model: this.config.deploymentName, // Use deployment name
input: processedText,
encoding_format: 'float',
});

// Update dimension from actual response
this.dimension = response.data[0].embedding.length;

return {
vector: response.data[0].embedding,
dimension: this.dimension
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';

// Provide specific error messages for common Azure issues
if (errorMessage.includes('API key') || errorMessage.includes('unauthorized')) {
throw new Error(`Azure OpenAI authentication failed: ${errorMessage}`);
}

if (errorMessage.includes('deployment') || errorMessage.includes('not found')) {
throw new Error(`Azure OpenAI deployment '${this.config.deploymentName}' not found: ${errorMessage}`);
}

if (errorMessage.includes('rate limit') || errorMessage.includes('quota')) {
throw new Error(`Azure OpenAI rate limit exceeded: ${errorMessage}`);
}

throw new Error(`Failed to generate Azure OpenAI embedding: ${errorMessage}`);
}
}

async embedBatch(texts: string[]): Promise<EmbeddingVector[]> {
const processedTexts = this.preprocessTexts(texts);

// Check if we need to detect dimension
const knownModels = AzureOpenAIEmbedding.getSupportedModels();
let needsDimensionDetection = true;

for (const [modelName, info] of Object.entries(knownModels)) {
const modelPattern = modelName.replace(/\./g, '-');
if (this.config.deploymentName.toLowerCase().includes(modelPattern)) {
this.dimension = info.dimension;
needsDimensionDetection = false;
break;
}
}

if (needsDimensionDetection && this.dimension === 1536) {
this.dimension = await this.detectDimension();
}

try {
const response = await this.client.embeddings.create({
model: this.config.deploymentName, // Use deployment name
input: processedTexts,
encoding_format: 'float',
});

// Update dimension from actual response
this.dimension = response.data[0].embedding.length;

return response.data.map((item) => ({
vector: item.embedding,
dimension: this.dimension
}));
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';

// Provide specific error messages for common Azure issues
if (errorMessage.includes('API key') || errorMessage.includes('unauthorized')) {
throw new Error(`Azure OpenAI authentication failed: ${errorMessage}`);
}

if (errorMessage.includes('deployment') || errorMessage.includes('not found')) {
throw new Error(`Azure OpenAI deployment '${this.config.deploymentName}' not found: ${errorMessage}`);
}

if (errorMessage.includes('rate limit') || errorMessage.includes('quota')) {
throw new Error(`Azure OpenAI rate limit exceeded: ${errorMessage}`);
}

throw new Error(`Failed to generate Azure OpenAI batch embeddings: ${errorMessage}`);
}
}

getDimension(): number {
// Check if deployment name matches known models
const knownModels = AzureOpenAIEmbedding.getSupportedModels();

for (const [modelName, info] of Object.entries(knownModels)) {
const modelPattern = modelName.replace(/\./g, '-');
if (this.config.deploymentName.toLowerCase().includes(modelPattern)) {
return info.dimension;
}
}

// For custom deployments, return the current dimension
// Note: This may be incorrect until detectDimension() is called
console.warn(`[AzureOpenAIEmbedding] ⚠️ getDimension() called for deployment '${this.config.deploymentName}' - returning ${this.dimension}. Call detectDimension() first for accurate dimension.`);
return this.dimension;
}

getProvider(): string {
return 'Azure OpenAI';
}

/**
* Set deployment name
* @param deploymentName Azure deployment name
*/
async setDeployment(deploymentName: string): Promise<void> {
this.config.deploymentName = deploymentName;

// Check if this matches a known model
const knownModels = AzureOpenAIEmbedding.getSupportedModels();
let foundKnownModel = false;

for (const [modelName, info] of Object.entries(knownModels)) {
const modelPattern = modelName.replace(/\./g, '-');
if (deploymentName.toLowerCase().includes(modelPattern)) {
this.dimension = info.dimension;
foundKnownModel = true;
break;
}
}

if (!foundKnownModel) {
// Detect dimension for custom deployment
this.dimension = await this.detectDimension();
}
}

/**
* Get client instance (for advanced usage)
*/
getClient(): AzureOpenAI {
return this.client;
}

/**
* Get list of supported models (these are OpenAI model names, not Azure deployment names)
* Azure deployments can be named anything, but often include the model name
*/
static getSupportedModels(): Record<string, { dimension: number; description: string }> {
return {
'text-embedding-3-small': {
dimension: 1536,
description: 'High performance and cost-effective embedding model (recommended)'
},
'text-embedding-3-large': {
dimension: 3072,
description: 'Highest performance embedding model with larger dimensions'
},
'text-embedding-ada-002': {
dimension: 1536,
description: 'Legacy model (use text-embedding-3-small instead)'
}
};
}
}
1 change: 1 addition & 0 deletions packages/core/src/embedding/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export * from './base-embedding';

// Implementation class exports
export * from './openai-embedding';
export * from './azure-openai-embedding';
export * from './voyageai-embedding';
export * from './ollama-embedding';
export * from './gemini-embedding';
Loading